-
-
Notifications
You must be signed in to change notification settings - Fork 213
Migrate Apollo Client to v4 #2178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Migrate Apollo Client to v4 #2178
Conversation
Summary by CodeRabbit
WalkthroughMigrate frontend to Apollo Client v4 and GraphQL codegen artifacts: update dependencies, switch hook imports to Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Tests will fail. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (10)
frontend/src/components/ModuleForm.tsx (1)
272-285
: Fix invalid Error construction and avoid rethrow inside debounced async.
new Error('...', err)
is invalid (Error ctor takes a single message). Rethrowing here can cause unhandled promise rejections and noisy UI. Prefer logging + user-facing error state.Apply:
try { const { data } = await client.query({ query: SearchProjectNamesDocument, variables: { query }, }) setRawResults(data.searchProjects || []) setShowSuggestions(true) } catch (err) { setRawResults([]) setShowSuggestions(false) - throw new Error('Error fetching suggestions:', err) + console.error('Error fetching suggestions:', err) + setError('Failed to fetch project names. Please try again.') }frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (1)
125-137
: Bug: Spinner masks the 403 view; check denied before showing the loaderDenied users currently see a spinner because the loader return runs first. Show the denied view first, then the loader only when allowed-but-not-ready.
Apply:
- if (accessStatus === 'checking' || !formData) { - return <LoadingSpinner /> - } - - if (accessStatus === 'denied') { + if (accessStatus === 'denied') { return ( <ErrorDisplay statusCode={403} title="Access Denied" message="You do not have permission to edit this module. You will be redirected." /> ) } + + if (accessStatus === 'checking' || (accessStatus === 'allowed' && !formData)) { + return <LoadingSpinner /> + }frontend/src/app/my/mentorship/page.tsx (2)
139-146
: Resolve the TODO by aligning ProgramCard’sprogram
prop to generated typesThe prop is currently a TODO; lock this down to the generated node type (or an adapter). Two options:
- Update ProgramCard’s prop to accept
MyProgram
(preferred).- Map
p
to a view-model that matches ProgramCard’s expected shape.If ProgramCard uses dates, this also depends on fixing the scalar mapping above.
I can adjust ProgramCard’s props and add a simple adapter if needed.
54-62
: Useundefined
for emptysearch
; consider disablingnotifyOnNetworkStatusChange
- In
frontend/src/app/my/mentorship/page.tsx
(around line 54), change- variables: { search: debouncedQuery, page, limit: 24 }, + variables: { search: debouncedQuery || undefined, page, limit: 24 },- Neither
frontend/src/utils/helpers/apolloClient.ts
norfrontend/src/server/apolloClient.ts
defines a globalnotifyOnNetworkStatusChange
, so addto this query’s options to restore v3-style loading behavior.notifyOnNetworkStatusChange: false
frontend/src/app/my/mentorship/programs/create/page.tsx (1)
76-85
: Harden error handling in catch (narrow unknown to ApolloError/Error).Avoid relying on
any
/optional chaining onunknown
. Show GraphQL/network details when available.Apply:
-import { useMutation } from '@apollo/client/react' +import { useMutation } from '@apollo/client/react' +import { isApolloError } from '@apollo/client' @@ - } catch (err) { + } catch (err: unknown) { + const message = + isApolloError(err) + ? err.message + : err instanceof Error + ? err.message + : 'Unable to complete the requested operation.' addToast({ - description: err?.message || 'Unable to complete the requested operation.', + description: message, title: 'GraphQL Request Failed', timeout: 3000, shouldShowTimeoutProgress: true, color: 'danger', variant: 'solid', })frontend/src/wrappers/provider.tsx (1)
27-31
: Guard against null Apollo client to prevent runtime crash.
createApolloClient()
can returnnull
(see utils/helpers/apolloClient.ts). Passing that intoApolloProvider
will throw.Minimal fix here:
+import { ErrorDisplay } from 'app/global-error' @@ export function Providers({ children }: { children: React.ReactNode }) { return ( <Suspense> <SessionProvider> <HeroUIProvider> <NextThemesProvider attribute="class" defaultTheme="dark"> <ToastProvider /> - <ApolloProvider client={apolloClient}> + {!apolloClient ? ( + <ErrorDisplay + statusCode={500} + title="GraphQL client unavailable" + message="Missing or invalid GRAPHQL_URL configuration." + /> + ) : ( + <ApolloProvider client={apolloClient}> <AppInitializer /> {children} - </ApolloProvider> + </ApolloProvider> + )} </NextThemesProvider> </HeroUIProvider> </SessionProvider> </Suspense> ) }Alternative (also valid): make
createApolloClient()
throw instead of returningnull
and surface a global error boundary.frontend/src/utils/helpers/apolloClient.ts (1)
7-11
: Avoid returning null from factory; fail fast or provide a clear fallback.Returning
null
leaks into consumers (see Providers) and causes crashes if unguarded.Option A (fail fast):
- if (!GRAPHQL_URL) { - const error = new AppError(500, 'Missing GraphQL URL') - handleAppError(error) - return null - } + if (!GRAPHQL_URL) { + const error = new AppError(500, 'Missing GraphQL URL') + handleAppError(error) + throw error + }If you keep returning
null
, ensure every consumer guards (see suggested Providers fix).frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (1)
29-38
: Fix: loading can hang forever when module is missing (return 404).If the query resolves with
getModule === null
and no GraphQL error,isLoading
never flips tofalse
, so the spinner never stops and the 404 UI never renders.Apply this minimal patch:
- useEffect(() => { - if (data?.getModule) { - setModule(data.getModule) - setAdmins(data.getProgram.admins) - setIsLoading(false) - } else if (error) { - handleAppError(error) - setIsLoading(false) - } - }, [data, error]) + useEffect(() => { + if (data) { + setModule(data.getModule ?? null) + setAdmins(data.getProgram.admins) + setIsLoading(false) + } else if (error) { + handleAppError(error) + setIsLoading(false) + } + }, [data, error])frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (1)
29-38
: Spinner never stops when module is missing (404 case).You only clear isLoading when data.getModule exists or when error is set. If the query returns successfully with getModule = null, the spinner persists forever.
Apply this diff:
- useEffect(() => { - if (data?.getModule) { - setModule(data.getModule) - setAdmins(data.getProgram.admins) - setIsLoading(false) - } else if (error) { - handleAppError(error) - setIsLoading(false) - } - }, [data, error]) + useEffect(() => { + if (data) { + setModule(data.getModule) + setAdmins(data.getProgram.admins) + setIsLoading(false) + } + if (error) { + handleAppError(error) + setIsLoading(false) + } + }, [data, error])Also applies to: 40-50
frontend/src/app/members/[memberKey]/page.tsx (1)
82-104
: Crash when user.bio is empty/undefined.map is called on possibly undefined (result of user?.bio?.split(...)). This throws at runtime.
Apply this diff:
- const formattedBio = user?.bio?.split(' ').map((word, index) => { + const formattedBio = user?.bio?.split(' ')?.map((word, index) => {Optionally, default to null to avoid rendering an empty
:
- const formattedBio = user?.bio?.split(' ')?.map(...) + const formattedBio = user?.bio ? user.bio.split(' ').map(...) : null
🧹 Nitpick comments (52)
frontend/src/components/ModuleForm.tsx (1)
272-279
: Optional: force fresh search results and guard against cache staleness.Search suggestions usually benefit from bypassing cache.
Apply:
- const { data } = await client.query({ + const { data } = await client.query({ query: SearchProjectNamesDocument, variables: { query }, + fetchPolicy: 'network-only', })frontend/src/hooks/useDjangoSession.ts (3)
25-28
: Nit: coerce shouldSync to a boolean.As written, shouldSync is boolean | string due to accessToken. Coerce to boolean for clarity.
- const shouldSync = - status === 'authenticated' && - (session as ExtendedSession)?.accessToken && - !sessionStorage.getItem(SYNC_STATUS_KEY) + const shouldSync = + status === 'authenticated' && + Boolean((session as ExtendedSession)?.accessToken) && + !sessionStorage.getItem(SYNC_STATUS_KEY)
45-49
: Avoid possible TS nullability complaint on session.user.Inside this block session is present, but TS won’t narrow. Assert via cast (or pre-destructure) to keep types happy.
- update({ - user: { - ...session.user, - isOwaspStaff: githubAuth.user?.isOwaspStaff, - }, - }).then(() => {}) + { + const { user } = session as ExtendedSession + update({ + user: { + ...user, + isOwaspStaff: githubAuth.user?.isOwaspStaff, + }, + }).then(() => {}) + }
50-59
: Toast may be lost due to signOut redirect.signOut() redirects by default, so the toast likely won’t be visible. Either show the toast before signing out, or use redirect: false.
- } else { - signOut() // Invalidate Next.js session if not ok. - addToast({ + } else { + addToast({ color: 'danger', description: githubAuth?.message, shouldShowTimeoutProgress: true, timeout: 4000, title: 'Authentication Failed', variant: 'bordered', }) + void signOut({ redirect: false }) // Keep user on page so toast is visible. }frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx (3)
37-70
: Avoid orphaned redirect timer; add cleanup in effectIf the component unmounts or dependencies change before the timeout fires, the pending redirect persists. Track and clear the timer.
Apply:
useEffect(() => { if (sessionStatus === 'loading' || queryLoading) { return } + let timeoutId: ReturnType<typeof setTimeout> | undefined + if ( queryError || !data?.getProgram || !data?.getModule || sessionStatus === 'unauthenticated' ) { setAccessStatus('denied') return } const currentUserLogin = (sessionData as ExtendedSession)?.user?.login const isAdmin = data.getProgram.admins?.some( (admin: { login: string }) => admin.login === currentUserLogin ) if (isAdmin) { setAccessStatus('allowed') } else { setAccessStatus('denied') addToast({ title: 'Access Denied', description: 'Only program admins can edit modules.', color: 'danger', variant: 'solid', timeout: 4000, }) - setTimeout(() => router.replace(`/my/mentorship/programs/${programKey}`), 1500) + timeoutId = setTimeout( + () => router.replace(`/my/mentorship/programs/${programKey}`), + 1500 + ) } -}, [sessionStatus, sessionData, queryLoading, data, programKey, queryError, router]) + return () => { + if (timeoutId) clearTimeout(timeoutId) + } +}, [sessionStatus, sessionData, queryLoading, data, programKey, queryError, router])
81-86
: Default projectName to an empty string to keep inputs controlledPrevents potential uncontrolled-to-controlled input warnings if projectName is undefined.
Apply:
- projectName: m.projectName, + projectName: m.projectName || '',
27-35
: Use generated typed documents for this query
Import the code-generatedGetProgramAdminsAndModulesDocument
and its correspondingGetProgramAdminsAndModulesQuery
/GetProgramAdminsAndModulesQueryVariables
types fromsrc/types/__generated__/moduleQueries.generated.ts
, then replace the legacyGET_PROGRAM_ADMINS_AND_MODULES
constant in youruseQuery
call with the generated document and supply the proper generics. This will ensure full type safety and consistency with the rest of the codebase.frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)
29-30
: Avoid duplicating server data in local state; prevent a 404 flicker on refetchBecause you set isRefetching=false before the useQuery data updates, users can briefly hit the 404 branch. Either derive program/modules directly from useQuery or set state from the awaited refetch() result.
Option A (simpler): derive
const { data } = useQuery(...) const program = data?.getProgram ?? null const modules = data?.getProgramModules ?? []Option B (keep state): hydrate from refetch result
const { data: d } = await refetch() setProgram(d?.getProgram ?? null) setModules(d?.getProgramModules ?? [])
33-36
: Replace TODO with fully typed mutation via codegenType the mutation to catch server/variable drift at compile time.
- // TODO: update type - const [updateProgram] = useMutation(UPDATE_PROGRAM_STATUS_MUTATION, { - onError: handleAppError, - }) + const [updateProgram] = useMutation< + UpdateProgramStatusMutation, + UpdateProgramStatusMutationVariables + >(UpdateProgramStatusDocument, { onError: handleAppError })Add the import (outside this hunk):
import { UpdateProgramStatusDocument, UpdateProgramStatusMutation, UpdateProgramStatusMutationVariables, } from 'types/__generated__/programsMutations.generated'
81-81
: Ensure UI waits for refetch before showing successTo avoid toasts firing before the UI updates, wait for refetch completion or use onQueryUpdated.
await updateProgram({ variables: { inputData: { key: program.key, name: program.name, status: newStatus, }, }, - refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }], + refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }], + awaitRefetchQueries: true, })In Apollo Client v4, refetchQueries supports passing DocumentNodes and variables; awaitRefetchQueries is still available, and returning a Promise from onQueryUpdated is the preferred alternative. (apollographql.com)
134-135
: Don’t assert dates to string; guard or map scalar to avoid runtime “Invalid date”Short-term guard (safer if nulls slip through):
- { label: 'Start Date', value: formatDate(program.startedAt as string) }, - { label: 'End Date', value: formatDate(program.endedAt as string) }, + { label: 'Start Date', value: program.startedAt ? formatDate(program.startedAt as unknown as number | string) : 'N/A' }, + { label: 'End Date', value: program.endedAt ? formatDate(program.endedAt as unknown as number | string) : 'N/A' },Long-term: map your Date/DateTime scalar in codegen (e.g., DateTime: string) so these casts disappear.
frontend/src/app/my/mentorship/page.tsx (1)
34-34
: Introduce a named alias for readability and reuseThe inline indexed access type is correct but hard to scan and reuse.
Apply:
-const [programs, setPrograms] = useState<GetMyProgramsQuery['myPrograms']['programs']>([]) +const [programs, setPrograms] = useState<MyProgram[]>([])Add near the imports:
type MyProgram = GetMyProgramsQuery['myPrograms']['programs'][number]frontend/src/app/page.tsx (1)
49-52
: Tighten types and loading checks (optional).
- State typing: allow null in the state to match the initial value.
- Loading gate: consider waiting on Algolia data too to avoid map flashing.
Apply:
- const [data, setData] = useState<MainPageData>(null) + const [data, setData] = useState<MainPageData | null>(null) - if (isLoading || !graphQLData || !geoLocData) { + if (isLoading || !graphQLData || geoLocData.length === 0) { return <LoadingSpinner /> }Also applies to: 94-96
frontend/src/components/CardDetailsPage.tsx (1)
38-38
: Convert the TODO into a concrete typing task.Two low-risk wins:
- Narrow prop types on SocialLinks.
- Ensure props with
.length
(e.g.,healthMetricsData
) are typed as arrays.Apply:
-export const SocialLinks = ({ urls }) => { +export const SocialLinks = ({ urls }: { urls: string[] }) => {If you’d like, I can open a follow-up issue to “type tighten” DetailsCardProps and children.
frontend/src/hooks/useLogout.ts (1)
1-1
: Import path is correct; consider using useApolloClient for cache ops.Sticking to the React entrypoint is right. For cache clearing, AC v4 recommends using the client directly via
useApolloClient()
for imperative work, which keeps the mutation tuple focused on UI state. UseclearStore()
(no refetch) orresetStore()
(refetch active queries) per your UX. (apollographql.com)Apply:
-import { useMutation } from '@apollo/client/react' +import { useMutation, useApolloClient } from '@apollo/client/react' @@ export const useLogout = () => { - const [logoutUser, { loading, client }] = useMutation(LOGOUT_DJANGO_MUTATION) + const [logoutUser, { loading }] = useMutation(LOGOUT_DJANGO_MUTATION) + const client = useApolloClient() @@ - await client.clearStore() // Removes Apollo cache + await client.clearStore() // Removes Apollo cache (no refetch)Note: per past learnings, some call sites intentionally invoke
logout()
andsignOut()
both—don’t dedupe unless you plan to adjust those call sites too.frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx (1)
2-2
: Correct migration: import hooks from @apollo/client/react.Matches AC v4 guidance. If/when you switch this page to codegen
TypedDocumentNode
s, AC v4 will infer variable/result types from the document directly. (apollographql.com)If you want, I can prep a follow-up patch to replace
CREATE_MODULE
/GET_PROGRAM_ADMIN_DETAILS
with generated...Document
artifacts for stronger typing.frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx (2)
70-75
: Use an anchor for external URLs (accessibility + expected behavior).Next.js Link is intended for internal routes.
- <Link href={repository.url} className="text-blue-400 hover:underline"> - {repository.url} - </Link> + <a + href={repository.url} + className="text-blue-400 hover:underline" + target="_blank" + rel="noopener noreferrer" + > + {repository.url} + </a>
30-41
: Tidy useEffect deps.
repositoryKey
isn’t referenced inside the effect; either remove it or includeorganizationKey
too for symmetry.- }, [data, graphQLRequestError, repositoryKey]) + }, [data, graphQLRequestError])frontend/src/utils/helpers/apolloClient.ts (1)
18-27
: Optional: centralize GraphQL error logging with an errorLink.Helps unify toasts/logging instead of per-call try/catch.
+import { onError } from '@apollo/client/link/error' @@ - const authLink = setContext(async (_, { headers }) => { + const authLink = setContext(async (_, { headers }) => { const csrfToken = await getCsrfToken() @@ }) + + const errorLink = onError(({ graphQLErrors, networkError }) => { + if (graphQLErrors?.length || networkError) { + handleAppError(new AppError(500, 'GraphQL Request Failed')) + } + }) @@ - return new ApolloClient({ + return new ApolloClient({ cache: new InMemoryCache(), - link: authLink.concat(httpLink), + link: errorLink.concat(authLink).concat(httpLink), })frontend/src/app/chapters/[chapterKey]/page.tsx (1)
70-70
: Address the TODO comment.The TODO indicates a type issue with the
geolocationData
prop.Would you like me to help resolve the type issue with the
geolocationData
prop in theDetailsCard
component?frontend/src/app/about/page.tsx (2)
68-73
: Reduce state duplication — derive UI straight from query results.
projectMetadata
andtopContributors
mirrordata
and require effects to sync. Prefer readingprojectMetadataResponse?.project
andtopContributorsResponse?.topContributors
directly to avoid extra renders and stale state.If you want, I can push a minimal diff that inlines these and drops both effects.
Also applies to: 75-94, 101-113
273-275
: Tighten type:user
is non-null here.After the null-guard above, make the parameter non-null to prevent accidental misuse elsewhere.
- const handleButtonClick = (user: GetLeaderDataQuery['user']) => { + const handleButtonClick = (user: NonNullable<GetLeaderDataQuery['user']>) => { router.push(`/members/${user.login}`) }frontend/src/app/organizations/[organizationKey]/page.tsx (2)
14-14
: Add strong types for local state from codegen.State is
any
-ish asnull
. UseGetOrganizationDataQuery
to keep props/consumers type-safe.-import { GetOrganizationDataDocument } from 'types/__generated__/organizationQueries.generated' +import { + GetOrganizationDataDocument, +} from 'types/__generated__/organizationQueries.generated' +import type { GetOrganizationDataQuery } from 'types/__generated__/organizationQueries.generated' - const [organization, setOrganization] = useState(null) - const [issues, setIssues] = useState(null) - const [milestones, setMilestones] = useState(null) - const [pullRequests, setPullRequests] = useState(null) - const [releases, setReleases] = useState(null) - const [repositories, setRepositories] = useState(null) - const [topContributors, setTopContributors] = useState(null) + const [organization, setOrganization] = + useState<GetOrganizationDataQuery['organization'] | null>(null) + const [issues, setIssues] = + useState<GetOrganizationDataQuery['recentIssues'] | null>(null) + const [milestones, setMilestones] = + useState<GetOrganizationDataQuery['recentMilestones'] | null>(null) + const [pullRequests, setPullRequests] = + useState<GetOrganizationDataQuery['recentPullRequests'] | null>(null) + const [releases, setReleases] = + useState<GetOrganizationDataQuery['recentReleases'] | null>(null) + const [repositories, setRepositories] = + useState<GetOrganizationDataQuery['repositories'] | null>(null) + const [topContributors, setTopContributors] = + useState<GetOrganizationDataQuery['topContributors'] | null>(null)Also applies to: 20-27
28-30
: Optional: rely onuseQuery
’sloading
instead of a localisLoading
state.This simplifies logic and prevents edge cases where local state and hook state drift.
I can provide a follow-up diff if you want this simplified.
Also applies to: 32-47, 49-54
frontend/src/app/snapshots/[id]/page.tsx (2)
169-172
: Keys and nullable dates.
- Use a stable key (
release.id
) instead oftagName-index
.- Guard
publishedAt
(nullable) to avoid “Released: ” with empty date.- {snapshot.newReleases.map((release, index) => ( + {snapshot.newReleases.map((release) => ( <div - key={`${release.tagName}-${index}`} + key={release.id} className="overflow-hidden rounded-lg border-1 border-gray-200 bg-white shadow-xs transition-all hover:shadow-md dark:border-gray-700 dark:bg-gray-800" > ... - Released: {formatDate(release.publishedAt as string)} + Released:{' '} + {release.publishedAt + ? formatDate(release.publishedAt as string) + : 'TBD'}Also applies to: 190-191
13-15
: Unify types with codegen to drop casts and TODOs.
renderProjectCard
/renderChapterCard
take app-local types while you pass codegen objects; plus you cast scalars tostring
. Prefer codegen types for the render helpers.-import type { Chapter } from 'types/chapter' -import type { Project } from 'types/project' +// Use codegen types to match the query payload +type SnapshotProject = NonNullable<GetSnapshotDetailsQuery['snapshot']>['newProjects'][number] +type SnapshotChapter = NonNullable<GetSnapshotDetailsQuery['snapshot']>['newChapters'][number] - const renderProjectCard = (project: Project) => { + const renderProjectCard = (project: SnapshotProject) => { ... - const renderChapterCard = (chapter: Chapter) => { + const renderChapterCard = (chapter: SnapshotChapter) => {Consider mapping GraphQL scalars in codegen (e.g., DateTime ->
string
) to eliminateas string
casts project-wide.Also applies to: 43-69, 71-97, 153-163
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)
17-17
: Prefer casting over a generic foruseParams
(Next.js types aren’t generic).Keeps consistency with other files and avoids type errors across Next versions.
- const { programKey, moduleKey } = useParams<{ programKey: string; moduleKey: string }>() + const { programKey, moduleKey } = useParams() as { programKey: string; moduleKey: string }
54-59
: Reduce “as string” casts by mapping date scalars in codegen.
startedAt/endedAt
areunknown
due to codegen config. Map your GraphQL Date/DateTime scalars tostring
in codegen to restore type-safety and drop the casts here.Example (codegen.yml):
config: scalars: DateTime: string Date: string
Then rerun codegen and remove the
as string
casts at call sites.
19-41
: Optional: derive UI state from Apollo instead of mirroring it.You can drop local
module/admins/isLoading
state and useconst { data, error, loading } = useQuery(...)
, then read fromdata
directly. Less state, fewer effects, fewer edge cases.frontend/src/app/snapshots/page.tsx (1)
16-26
: Optional: use Apollo’sloading
and avoid localisLoading
/effect bookkeeping.Simplifies control flow and removes a class of race conditions around manual loading flags.
- const [snapshots, setSnapshots] = useState<GetCommunitySnapshotsQuery['snapshots'] | null>(null) - const [isLoading, setIsLoading] = useState<boolean>(true) - - const { data: graphQLData, error: graphQLRequestError } = useQuery(GetCommunitySnapshotsDocument) + const { data: graphQLData, error: graphQLRequestError, loading } = + useQuery(GetCommunitySnapshotsDocument) + const snapshots = graphQLData?.snapshots ?? null @@ - if (graphQLData) { - setSnapshots(graphQLData.snapshots) - setIsLoading(false) - } if (graphQLRequestError) { addToast({ @@ - setIsLoading(false) } }, [graphQLData, graphQLRequestError]) @@ - if (isLoading) { + if (loading) { return <LoadingSpinner /> }Also applies to: 63-66
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx (2)
77-79
: Dropas string
casts by mapping date scalars in codegen.Same rationale as other files; makes
formatDateForInput
calls type-safe.- startedAt: formatDateForInput(program.startedAt as string), - endedAt: formatDateForInput(program.endedAt as string), + startedAt: formatDateForInput(program.startedAt), + endedAt: formatDateForInput(program.endedAt),(After adding
DateTime: string
mapping in codegen.)
19-20
: Consistency nit: matchuseParams
typing style used across pages.Here you cast; in modules page you used a generic. Pick one (casting is safer with Next types) to avoid drift.
frontend/src/app/projects/[projectKey]/page.tsx (2)
21-21
: Prefer Apollo’s loading state over bespoke isLoading.Leverage the loading flag from useQuery to avoid manual state bookkeeping and edge cases on param changes.
Apply this diff:
- const [isLoading, setIsLoading] = useState<boolean>(true) + // Rely on Apollo's loading flag const { data, error: graphQLRequestError } = useQuery(GetProjectDocument, { variables: { key: projectKey }, }) useEffect(() => { if (data) { setProject(data.project) setTopContributors(data.topContributors) - setIsLoading(false) } if (graphQLRequestError) { handleAppError(graphQLRequestError) - setIsLoading(false) } }, [data, graphQLRequestError, projectKey]) - if (isLoading) { + // Show spinner while the query is loading + if (!project && !graphQLRequestError) { return <LoadingSpinner /> }Also applies to: 27-37, 39-41
27-37
: Unnecessary dependency.projectKey in the effect dependencies isn’t used inside the effect and will cause extra runs on param changes.
- }, [data, graphQLRequestError, projectKey]) + }, [data, graphQLRequestError])frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2)
19-19
: Type admins state explicitly.Avoid implicit any; align with generated types.
- const [admins, setAdmins] = useState(null) + const [admins, setAdmins] = useState< + GetProgramAdminsAndModulesQuery['getProgram']['admins'] + >(null)
22-27
: Use Apollo loading instead of manual isLoading.Same suggestion as other pages for consistency and fewer states.
- const [isLoading, setIsLoading] = useState(true) - const { data, error } = useQuery(GetProgramAdminsAndModulesDocument, { + const { data, error, loading } = useQuery(GetProgramAdminsAndModulesDocument, { variables: { programKey, moduleKey }, }) @@ - if (isLoading) return <LoadingSpinner /> + if (loading) return <LoadingSpinner />Also applies to: 40-41
frontend/src/app/members/[memberKey]/page.tsx (1)
23-31
: Minor state/flow cleanups.
- Initialize user to null to avoid undefined in unions.
- Prefer Apollo loading flag over manual isLoading for consistency.
- const [user, setUser] = useState<GetUserDataQuery['user'] | null>() + const [user, setUser] = useState<GetUserDataQuery['user'] | null>(null) @@ - const [isLoading, setIsLoading] = useState<boolean>(true) + // Rely on Apollo 'loading' @@ - const { data: graphQLData, error: graphQLRequestError } = useQuery(GetUserDataDocument, { + const { data: graphQLData, error: graphQLRequestError, loading } = useQuery(GetUserDataDocument, { variables: { key: memberKey }, }) @@ - if (isLoading) { + if (loading) { return <LoadingSpinner /> } @@ - if (!isLoading && user == null) { + if (!loading && user == null) { return (Also applies to: 106-118, 211-225
frontend/src/app/settings/api-keys/page.tsx (10)
37-37
: Small type alias to reduce repetitionDefine an alias for readability and reuse.
-const [keyToRevoke, setKeyToRevoke] = useState<GetApiKeysQuery['apiKeys'][0] | null>(null) +type ApiKey = GetApiKeysQuery['apiKeys'][number] +const [keyToRevoke, setKeyToRevoke] = useState<ApiKey | null>(null)
39-42
: Don’t block on errors when errorPolicy='all'With errorPolicy: 'all', you may have both data and error; current ternary hides partial data. Gate the error block on !data.
- {error ? ( + {error && !data ? ( <div className="rounded-md bg-red-50 p-4 text-red-700 dark:bg-red-900/20 dark:text-red-400"> Error loading API keys </div> ) : loading ? (Optionally surface a non-blocking banner when error && data.
Also applies to: 217-229
105-114
: Validate expiry and send ISO string to avoid TZ ambiguityEnsure date isn’t in the past and serialize as ISO (servers usually expect strings).
- const variables: { name: string; expiresAt: Date } = { - name: newKeyName.trim(), - expiresAt: new Date(newKeyExpiry), - } - createApiKey({ variables }) + const expiry = new Date(newKeyExpiry) + const today = new Date(); today.setHours(0,0,0,0) + if (isNaN(expiry.getTime()) || expiry < today) { + addToast({ title: 'Error', description: 'Expiration must be today or later', color: 'danger' }) + return + } + createApiKey({ + variables: { name: newKeyName.trim(), expiresAt: expiry.toISOString() }, + })Consider mapping scalars in codegen so DateTime/UUID are typed as strings (see separate comment).
68-76
: Disable revoke button while mutation is in flightPrevents double submissions and accidental repeats.
-const [revokeApiKey] = useMutation(RevokeApiKeyDocument, { +const [revokeApiKey, { loading: revokeLoading }] = useMutation(RevokeApiKeyDocument, { onCompleted: () => {- <Button color="danger" onPress={handleRevokeKey}> + <Button color="danger" onPress={handleRevokeKey} isDisabled={revokeLoading}> Revoke Key </Button>Also applies to: 430-432
80-81
: Compute default expiry on open to avoid stale “30 days from today”Minor UX nit if the page stays open across midnight.
-const defaultExpiryDate = format(addDays(new Date(), 30), 'yyyy-MM-dd') ... - setNewKeyExpiry(defaultExpiryDate) + setNewKeyExpiry(format(addDays(new Date(), 30), 'yyyy-MM-dd'))Also applies to: 137-140
242-252
: Prefer stable string types and avoid casts; consider codegen scalar mappingUse id (string) for React key, and coerce with String(...) over as-casts. Also, map GraphQL scalars to real TS types to remove unknowns.
-{data.apiKeys.map((key: GetApiKeysQuery['apiKeys'][0]) => ( +{data.apiKeys.map((key: GetApiKeysQuery['apiKeys'][number]) => ( <tr - key={key.uuid as string} + key={key.id} className="border-b border-b-gray-200 dark:border-b-gray-700" > <td className="py-3">{key.name}</td> - <td className="py-3 font-mono text-sm">{key.uuid as string}</td> - <td className="py-3">{format(new Date(key.createdAt as string), 'PP')}</td> + <td className="py-3 font-mono text-sm">{String(key.uuid)}</td> + <td className="py-3">{format(new Date(String(key.createdAt)), 'PP')}</td> <td className="py-3"> - {key.expiresAt ? format(new Date(key.expiresAt as string), 'PP') : 'Never'} + {key.expiresAt ? format(new Date(String(key.expiresAt)), 'PP') : 'Never'} </td>Codegen (typescript plugin) suggestion to remove unknowns:
# codegen.yml generates: frontend/src/types/__generated__/: plugins: - typescript - typescript-operations config: scalars: DateTime: input: string output: string UUID: input: string output: string
234-234
: Tailwind class nit: 'border-b-1' isn’t a default utilityUse 'border-b' or an arbitrary value 'border-b-[1px]'.
-<tr className="border-b-1 border-b-gray-200 dark:border-b-gray-700"> +<tr className="border-b border-b-gray-200 dark:border-b-gray-700">
192-195
: Wording: use “revoke” to match the actionAlign the guidance with the button/action label.
-You've reached the maximum number of API keys. Delete an existing key to create +You've reached the maximum number of API keys. Revoke an existing key to create
150-158
: Type safety when passing uuid to mutationuuid is typed unknown in generated types; explicit string coercion avoids TS complaints and accidental non-string values.
- await revokeApiKey({ variables: { uuid: keyToRevoke.uuid } }) + await revokeApiKey({ variables: { uuid: String(keyToRevoke.uuid) } })
248-251
: Potential date off-by-one in some timezonesIf backend stores midnight UTC, local formatting can show the prior day. Consider storing/displaying local midnight or using date-fns-tz’s formatInTimeZone to render in UTC.
Would you like a patch using date-fns-tz (formatInTimeZone) to render consistently?
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (2)
42-52
: Optional: avoid mirroring server data in local stateYou can render directly from
data?.project
and drop theuseEffect
+ twouseState
s to reduce re-renders and state sync risks. Happy to draft a refactor if you want.
58-64
: Guard scalar parsing instead ofunknown
→string
cast
createdAt
isunknown
(custom scalar). Prefer safe coercion or map the scalar tostring
in codegen.- const labels = - metricsList?.map((m) => - new Date(m.createdAt as string).toLocaleString('default', { + const labels = + metricsList?.map(({ createdAt }) => + new Date(String(createdAt)).toLocaleString('default', { month: 'short', day: 'numeric', }) ) || []Alternatively, map your GraphQL Date/DateTime scalar to
string
in codegen.frontend/src/app/projects/dashboard/metrics/page.tsx (3)
89-93
: Nit: remove redundant||
fallback
currentOrdering
is always truthy. Drop the||
to simplify.- const [ordering, setOrdering] = useState( - currentOrdering || { - score: Ordering.Desc, - } - ) + const [ordering, setOrdering] = useState(currentOrdering)
98-115
: Consider explicit error handling policy with v4 defaultsIn v4,
notifyOnNetworkStatusChange
defaults totrue
, and error handling semantics are unified. If you expect partial data alongside errors, seterrorPolicy: 'all'
. Otherwise, you may see more loading transitions. (apollographql.com)} = useQuery(GetProjectHealthMetricsDocument, { variables: { filters, pagination: { offset: 0, limit: PAGINATION_LIMIT }, ordering: [ ordering, { // eslint-disable-next-line @typescript-eslint/naming-convention project_Name: Ordering.Asc, }, ], }, + // Optional: opt into partial data + errors + // errorPolicy: 'all', })
244-245
: Follow-up: updateMetricsCard
prop types, remove TODODefine
metric
as the generated item type to get full type coverage.Example (in MetricsCard.tsx):
import type { GetProjectHealthMetricsQuery } from 'types/__generated__/projectsHealthDashboardQueries.generated' type MetricT = GetProjectHealthMetricsQuery['projectHealthMetrics'][number]; export interface MetricsCardProps { metric: MetricT; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
frontend/pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
frontend/src/types/__generated__/userQueries.generated.ts
is excluded by!**/__generated__/**
📒 Files selected for processing (32)
frontend/package.json
(2 hunks)frontend/src/app/about/page.tsx
(6 hunks)frontend/src/app/chapters/[chapterKey]/page.tsx
(2 hunks)frontend/src/app/committees/[committeeKey]/page.tsx
(2 hunks)frontend/src/app/members/[memberKey]/page.tsx
(3 hunks)frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(2 hunks)frontend/src/app/mentorship/programs/[programKey]/page.tsx
(3 hunks)frontend/src/app/my/mentorship/page.tsx
(5 hunks)frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
(3 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
(1 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(2 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
(1 hunks)frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
(5 hunks)frontend/src/app/my/mentorship/programs/create/page.tsx
(1 hunks)frontend/src/app/organizations/[organizationKey]/page.tsx
(3 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx
(1 hunks)frontend/src/app/page.tsx
(1 hunks)frontend/src/app/projects/[projectKey]/page.tsx
(2 hunks)frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
(4 hunks)frontend/src/app/projects/dashboard/metrics/page.tsx
(6 hunks)frontend/src/app/projects/dashboard/page.tsx
(3 hunks)frontend/src/app/settings/api-keys/page.tsx
(5 hunks)frontend/src/app/snapshots/[id]/page.tsx
(5 hunks)frontend/src/app/snapshots/page.tsx
(2 hunks)frontend/src/components/CardDetailsPage.tsx
(1 hunks)frontend/src/components/ModuleForm.tsx
(2 hunks)frontend/src/hooks/useDjangoSession.ts
(2 hunks)frontend/src/hooks/useLogout.ts
(1 hunks)frontend/src/server/apolloClient.ts
(2 hunks)frontend/src/server/queries/userQueries.ts
(1 hunks)frontend/src/utils/helpers/apolloClient.ts
(2 hunks)frontend/src/wrappers/provider.tsx
(1 hunks)
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
frontend/src/app/chapters/[chapterKey]/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
frontend/src/app/projects/[projectKey]/page.tsx
frontend/src/app/organizations/[organizationKey]/page.tsx
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
frontend/src/app/organizations/[organizationKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/components/ModuleForm.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-11T15:46:58.233Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/UserMenu.tsx:56-60
Timestamp: 2025-07-11T15:46:58.233Z
Learning: In the UserMenu component's handleLogout function, both logout() and signOut() calls are intentionally kept despite the useLogout hook already calling signOut internally, as this is necessary for the specific implementation requirements.
Applied to files:
frontend/src/hooks/useLogout.ts
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
📚 Learning: 2025-07-12T17:14:28.536Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/[programKey]/edit/page.tsx:90-128
Timestamp: 2025-07-12T17:14:28.536Z
Learning: Both ProgramForm (programCard.tsx) and ModuleForm (mainmoduleCard.tsx) components already implement HTML validation using the `required` attribute on form fields. The browser's native validation prevents form submission and displays error messages when required fields are empty, eliminating the need for additional JavaScript validation before GraphQL mutations.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
frontend/src/app/projects/dashboard/metrics/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
frontend/src/app/projects/dashboard/metrics/page.tsx
frontend/src/app/projects/dashboard/page.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.
Applied to files:
frontend/src/app/members/[memberKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
🧬 Code graph analysis (20)
frontend/src/app/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
GetProgramAndModulesDocument
(37-37)GetProgramAndModulesQuery
(25-25)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/components/ModuleForm.tsx (1)
frontend/src/types/__generated__/projectQueries.generated.ts (1)
SearchProjectNamesDocument
(39-39)
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (1)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (2)
GetProjectHealthMetricsDetailsQuery
(23-23)GetProjectHealthMetricsDetailsDocument
(28-28)
frontend/src/app/settings/api-keys/page.tsx (1)
frontend/src/types/__generated__/apiKeyQueries.generated.ts (4)
GetApiKeysQuery
(7-7)GetApiKeysDocument
(25-25)CreateApiKeyDocument
(26-26)RevokeApiKeyDocument
(27-27)
frontend/src/hooks/useDjangoSession.ts (1)
frontend/src/types/__generated__/authQueries.generated.ts (1)
SyncDjangoSessionDocument
(18-18)
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx (2)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
GetProgramDetailsDocument
(36-36)frontend/src/utils/dateFormatter.ts (1)
formatDateForInput
(63-70)
frontend/src/app/members/[memberKey]/page.tsx (1)
frontend/src/types/__generated__/userQueries.generated.ts (2)
GetUserDataQuery
(16-16)GetUserDataDocument
(27-27)
frontend/src/app/projects/dashboard/metrics/page.tsx (1)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (2)
GetProjectHealthMetricsQuery
(16-16)GetProjectHealthMetricsDocument
(27-27)
frontend/src/app/about/page.tsx (2)
frontend/src/types/__generated__/projectQueries.generated.ts (4)
GetProjectMetadataDocument
(37-37)GetTopContributorsDocument
(38-38)GetProjectMetadataQuery
(16-16)GetTopContributorsQuery
(26-26)frontend/src/types/__generated__/userQueries.generated.ts (2)
GetLeaderDataDocument
(26-26)GetLeaderDataQuery
(9-9)
frontend/src/app/chapters/[chapterKey]/page.tsx (2)
frontend/src/types/__generated__/chapterQueries.generated.ts (2)
GetChapterDataQuery
(9-9)GetChapterDataDocument
(19-19)frontend/src/types/contributor.ts (1)
Contributor
(1-8)
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)
frontend/src/types/__generated__/moduleQueries.generated.ts (2)
GetProgramAdminsAndModulesQuery
(25-25)GetProgramAdminsAndModulesDocument
(30-30)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)frontend/src/components/ModuleCard.tsx (1)
getSimpleDuration
(98-110)
frontend/src/app/snapshots/[id]/page.tsx (2)
frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
GetSnapshotDetailsQuery
(9-9)GetSnapshotDetailsDocument
(24-24)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/app/my/mentorship/page.tsx (1)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
GetMyProgramsQuery
(11-11)GetMyProgramsDocument
(35-35)
frontend/src/app/projects/dashboard/page.tsx (1)
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (2)
GetProjectHealthStatsQuery
(7-7)GetProjectHealthStatsDocument
(26-26)
frontend/src/app/snapshots/page.tsx (2)
backend/apps/owasp/api/internal/queries/snapshot.py (2)
snapshots
(25-31)snapshot
(14-22)frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
GetCommunitySnapshotsQuery
(21-21)GetCommunitySnapshotsDocument
(26-26)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
GetProgramAndModulesQuery
(25-25)GetProgramAndModulesDocument
(37-37)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/app/projects/[projectKey]/page.tsx (1)
frontend/src/types/__generated__/projectQueries.generated.ts (2)
GetProjectQuery
(9-9)GetProjectDocument
(36-36)
frontend/src/app/committees/[committeeKey]/page.tsx (3)
backend/apps/owasp/api/internal/queries/committee.py (1)
committee
(14-28)frontend/src/types/__generated__/committeeQueries.generated.ts (2)
GetCommitteeDataQuery
(9-9)GetCommitteeDataDocument
(19-19)frontend/src/types/contributor.ts (1)
Contributor
(1-8)
frontend/src/app/organizations/[organizationKey]/page.tsx (1)
frontend/src/types/__generated__/organizationQueries.generated.ts (1)
GetOrganizationDataDocument
(19-19)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)
frontend/src/types/__generated__/moduleQueries.generated.ts (2)
GetProgramAdminsAndModulesQuery
(25-25)GetProgramAdminsAndModulesDocument
(30-30)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)frontend/src/components/ModuleCard.tsx (1)
getSimpleDuration
(98-110)
🪛 Biome (2.1.2)
frontend/src/app/snapshots/[id]/page.tsx
[error] 147-147: Wrap comments inside children within braces.
Unsafe fix: Wrap the comments with braces
(lint/suspicious/noCommentText)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run frontend e2e tests
- GitHub Check: Run backend tests
- GitHub Check: Run frontend unit tests
- GitHub Check: CodeQL (javascript-typescript)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (2)
frontend/src/app/snapshots/page.tsx (1)
40-42
: Prefer a readable item alias and letmap
infer the element type.Use a
SnapshotItem
alias ([number]
index) and drop the explicit element type inmap
. This mirrors prior feedback.Apply this diff:
- const handleButtonClick = (snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => { + const handleButtonClick = (snapshot: SnapshotItem) => { router.push(`/snapshots/${snapshot.key}`) }- const renderSnapshotCard = (snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => { + const renderSnapshotCard = (snapshot: SnapshotItem) => {- snapshots.map((snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => ( + snapshots.map((snapshot) => ( <div key={snapshot.key}>{renderSnapshotCard(snapshot)}</div> ))Add this alias near the imports (outside the diffed ranges):
type SnapshotItem = GetCommunitySnapshotsQuery['snapshots'][number]Also applies to: 44-50, 73-75
frontend/src/app/snapshots/[id]/page.tsx (1)
144-145
: Remove stray whitespace text node to satisfy Biome rule.This is the same issue previously flagged; the {' '} child is unnecessary here.
- {' '} {snapshot.newChapters.filter((chapter) => chapter.isActive).map(renderChapterCard)}
🧹 Nitpick comments (14)
frontend/src/utils/utility.ts (2)
7-7
: Use type-only import to avoid emitting runtime code.Switch to a type import for the generated GraphQL type.
-import { GetSnapshotDetailsQuery } from 'types/__generated__/snapshotQueries.generated' +import type { GetSnapshotDetailsQuery } from 'types/__generated__/snapshotQueries.generated'
29-36
: Harden createdAt handling and tighten types for params/reduce accumulator.
- Accept only known icon keys in
params
.- Support both numeric (sec/ms) and string timestamps for
createdAt
.- Type the reducer accumulator as
Partial<Icon>
to avoid{}
-as-Icon casts leaking.-export const getFilteredIcons = (project: ProjectType, params: string[]): Icon => { - const filteredIcons = params.reduce((acc: Icon, key) => { +export const getFilteredIcons = (project: ProjectType, params: readonly IconKeys[]): Icon => { + const filteredIcons = params.reduce<Partial<Icon>>((acc, key) => { if (ICONS[key as IconKeys] && project[key as keyof typeof project] !== undefined) { if (key === 'createdAt') { - acc[key] = dayjs.unix(project[key as keyof ProjectType] as unknown as number).fromNow() + const raw = project[key as keyof ProjectType] as unknown + const d = + typeof raw === 'number' + ? (raw > 1_000_000_000_000 ? dayjs(raw) : dayjs.unix(raw)) + : dayjs(raw as string) + if (d.isValid()) acc[key] = d.fromNow() } else { - acc[key] = project[key as keyof typeof project] as unknown as number + acc[key] = project[key as keyof ProjectType] as unknown as number } } return acc - }, {}) + }, {}) - return filteredIcons + return filteredIcons as Icon }Also applies to: 41-41
frontend/src/app/snapshots/page.tsx (2)
15-19
: Remove mirrored state; rely on Apollo’sloading
/data
to simplify and avoid effect churn.
isLoading
andsnapshots
duplicateuseQuery
state and add unnecessary effects (can also double-fire toasts under React StrictMode). Derive fromuseQuery
instead.Apply this diff:
- const [snapshots, setSnapshots] = useState<GetCommunitySnapshotsQuery['snapshots'] | null>(null) - const [isLoading, setIsLoading] = useState<boolean>(true) - - const { data: graphQLData, error: graphQLRequestError } = useQuery(GetCommunitySnapshotsDocument) + const { data, error, loading } = useQuery(GetCommunitySnapshotsDocument) + const snapshots = data?.snapshots ?? []- useEffect(() => { - if (graphQLData) { - setSnapshots(graphQLData.snapshots) - setIsLoading(false) - } - if (graphQLRequestError) { - addToast({ - description: 'Unable to complete the requested operation.', - title: 'GraphQL Request Failed', - timeout: 3000, - shouldShowTimeoutProgress: true, - color: 'danger', - variant: 'solid', - }) - setIsLoading(false) - } - }, [graphQLData, graphQLRequestError]) + useEffect(() => { + if (error) { + addToast({ + description: 'Unable to complete the requested operation.', + title: 'GraphQL Request Failed', + timeout: 3000, + shouldShowTimeoutProgress: true, + color: 'danger', + variant: 'solid', + }) + } + }, [error])- if (isLoading) { + if (loading) { return <LoadingSpinner /> }- {!snapshots?.length ? ( + {!snapshots.length ? ( <div className="col-span-full py-8 text-center">No Snapshots found</div> ) : ( snapshots.map((snapshot: GetCommunitySnapshotsQuery['snapshots'][0]) => ( <div key={snapshot.key}>{renderSnapshotCard(snapshot)}</div> )) )}Additionally, remove
useState
from the React import:- import React, { useState, useEffect } from 'react' + import React, { useEffect } from 'react'Also applies to: 20-36, 62-64, 70-76
53-53
: Avoid double keys and an extra wrapper node.You set
key
on<SnapshotCard>
and also on the wrapping<div>
. Keep one. Easiest: remove the wrapper and map directly to the card-returning renderer.Apply this diff:
- snapshots.map((snapshot) => ( - <div key={snapshot.key}>{renderSnapshotCard(snapshot)}</div> - )) + snapshots.map(renderSnapshotCard)Also applies to: 73-75
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5)
29-31
: Avoid mirroring Apollo data in local state.Storing
program
/modules
in state and syncing viauseEffect
risks staleness and extra renders. Prefer deriving directly fromdata
.Example (outside this hunk): after the
useQuery(...)
call, derive:const program = data?.getProgram ?? null const modules = data?.getProgramModules ?? []Then remove the
setProgram
/setModules
calls in the effect.
41-45
: Consider cache-and-network for snappier UX.If acceptable, fetch from cache first while updating in background.
} = useQuery(GetProgramAndModulesDocument, { variables: { programKey }, skip: !programKey, notifyOnNetworkStatusChange: true, + fetchPolicy: 'cache-and-network', })
144-144
: Resolve TODO: type modules prop and align accessLevel with actual role.
- Update
DetailsCard
prop to acceptGetProgramAndModulesQuery['getProgramModules']
to drop the TODO.- Consider
accessLevel={isAdmin ? 'admin' : 'read'}
to avoid overexposing actions when the viewer isn’t an admin.I can open a follow-up to type
DetailsCard
correctly and threadaccessLevel
fromisAdmin
. Want me to draft it?
2-2
: Import Apollo hooks from@apollo/client/react
everywhere. Test files are still importing hooks (e.g.useQuery
,useMutation
,useApolloClient
) from@apollo/client
; update those to the@apollo/client/react
subpath to maintain repo-wide consistency and avoid mixed entry points.
71-81
: Add awaitRefetchQueries to the mutation call
Ensure the mutation’s returned promise waits for the refetch by addingawaitRefetchQueries: true
alongsiderefetchQueries
—the input shape already includesname
, so no changes needed there.frontend/src/app/snapshots/[id]/page.tsx (5)
9-12
: Good switch to generated Document and typed query.This enables end-to-end type inference for useQuery.
If possible, also export and use the generated GetSnapshotDetailsQueryVariables for extra safety on variables.
21-22
: Tighten snapshot state type.GetSnapshotDetailsQuery['snapshot'] already includes null, so the extra "| null" is redundant.
- const [snapshot, setSnapshot] = useState<GetSnapshotDetailsQuery['snapshot'] | null>(null) + const [snapshot, setSnapshot] = useState<GetSnapshotDetailsQuery['snapshot']>(null)
26-28
: Guard the query until the route param is available; consider relying on useQuery.loading.Add skip: !snapshotKey to avoid a spurious request with undefined variables. Optional: favor loading from useQuery over a parallel isLoading state/effect to reduce race conditions.
- const { data: graphQLData, error: graphQLRequestError } = useQuery(GetSnapshotDetailsDocument, { - variables: { key: snapshotKey }, - }) + const { data: graphQLData, error: graphQLRequestError } = useQuery(GetSnapshotDetailsDocument, { + variables: { key: snapshotKey }, + skip: !snapshotKey, + })
186-186
: Handle nullable publishedAt without string cast; prefer a readable fallback.Also consider using release.id as the React key instead of tag/index (outside this hunk).
- Released: {formatDate(release.publishedAt as string)} + Released: {release.publishedAt ? formatDate(release.publishedAt as number | string) : 'TBD'}Outside this hunk, change the item key to a stable id:
key={release.id}
9-12
: Codegen scalars mapping to remove “unknown” dates (one-time config).Map server date/timestamp scalars to concrete TS types so casts disappear and type-safety improves.
Add to codegen config:
generates: frontend/src/types/__generated__/: plugins: - typescript - typescript-operations config: scalars: DateTime: string Timestamp: number Date: stringThen re-run codegen and replace the remaining as number | string casts.
Also applies to: 123-123, 186-186
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
(5 hunks)frontend/src/app/snapshots/[id]/page.tsx
(7 hunks)frontend/src/app/snapshots/page.tsx
(4 hunks)frontend/src/utils/utility.ts
(2 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
🧬 Code graph analysis (4)
frontend/src/utils/utility.ts (3)
frontend/src/types/project.ts (1)
Project
(17-45)frontend/src/types/chapter.ts (1)
Chapter
(3-19)frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
GetSnapshotDetailsQuery
(9-9)
frontend/src/app/snapshots/[id]/page.tsx (2)
frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
GetSnapshotDetailsQuery
(9-9)GetSnapshotDetailsDocument
(24-24)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)
frontend/src/types/__generated__/programsQueries.generated.ts (2)
GetProgramAndModulesQuery
(25-25)GetProgramAndModulesDocument
(37-37)frontend/src/types/__generated__/programsMutations.generated.ts (1)
UpdateProgramStatusDocument
(28-28)frontend/src/app/global-error.tsx (1)
handleAppError
(66-86)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/app/snapshots/page.tsx (2)
backend/apps/owasp/api/internal/queries/snapshot.py (2)
snapshots
(25-31)snapshot
(14-22)frontend/src/types/__generated__/snapshotQueries.generated.ts (2)
GetCommunitySnapshotsQuery
(21-21)GetCommunitySnapshotsDocument
(26-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run backend tests
- GitHub Check: Run frontend e2e tests
- GitHub Check: Run frontend unit tests
- GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (5)
frontend/src/app/snapshots/page.tsx (2)
2-2
: Apollo v4 import path is correct.Importing
useQuery
from@apollo/client/react
is v4-compliant. No action needed.
7-10
: Good move to codegen documents and types.Using
GetCommunitySnapshotsDocument
andGetCommunitySnapshotsQuery
tightens type-safety.frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)
9-14
: Switch to codegen documents/types — LGTM.Using generated DocumentNodes and enums tightens type-safety and aligns with the migration plan. Also compatible with the prior learning that GET_PROGRAM_DETAILS remains in use elsewhere; no conflict here.
33-35
: Mutation hook wiring — LGTM.
onError: handleAppError
centralizes error handling; keep it.frontend/src/app/snapshots/[id]/page.tsx (1)
2-2
: Apollo v4 React import looks good.Using the React-only subpath is compatible with v4 and keeps the bundle lean.
13c6f52
to
b8a110c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/app/snapshots/[id]/layout.tsx (1)
10-16
: Incorrect params typing/await in generateMetadata—use plain object.Next.js passes params as a plain object; remove Promise typing/await to match runtime and typings.
Apply this diff:
export async function generateMetadata({ params, }: { - params: Promise<{ id: string }> + params: { id: string } }): Promise<Metadata> { - const { id: snapshotKey } = await params + const { id: snapshotKey } = params const { data } = await apolloClient.query({ query: GetSnapshotDetailsMetadataDocument, variables: { key: snapshotKey }, })
🧹 Nitpick comments (20)
frontend/src/types/chapter.ts (1)
5-18
: Avoid blanket-optionalizing Chapter; prefer query-specific/generated types or local Partial/Pick.Most Chapter fields shouldn't be blanket-optional — keep Chapter strict and either use GraphQL‑codegen selection types per query (Apollo v4) or introduce a derived partial for call sites. Suggested helper: export type ChapterPartial = Partial
frontend/src/types/committee.ts (1)
16-16
: Make topContributors required (or explicitly nullable)GraphQL codegen shows topContributors as a non-null array — prefer a required array and normalise upstream to [] (or use explicit null union if the schema can return null).
Location: frontend/src/types/committee.ts:16
Suggested change:
- topContributors?: Contributor[] + topContributors: Contributor[] // ensure upstream maps undefined/null to []Call sites/tests to update: frontend/tests/unit/components/CardDetailsPage.test.tsx:1074 and 1195-1196 (these pass topContributors={undefined}).
frontend/src/types/pullRequest.ts (2)
3-5
: Document why author is nullable.A brief note will prevent churn and clarify expectations for consumers.
Apply:
export type PullRequest = { - author?: User + // author may be undefined if not returned by the API or omitted from the selection set + author?: User
3-10
: Optional: keep domain types strict; narrow at query boundaries.If this nullability is only due to specific queries, consider mapping API results to a stricter UI/domain type (with a fallback author) at the data layer instead of loosening the shared type. Given the migration focus, feel free to defer.
frontend/src/types/healthMetrics.ts (1)
30-30
: Self-documentcreatedAt
format (optional)If this is ISO‑8601, consider a branded alias to improve readability and reduce misuse.
Apply:
+export type ISODateString = string // e.g., 2025-09-22T12:34:56Z export type HealthMetricsProps = { ... - createdAt?: string + createdAt?: ISODateString ... }frontend/src/types/mentorship.ts (1)
2-3
: Import these enums as types; optionally re-export to avoid eslint disableGenerated file exports runtime enums, but this file uses them only in type positions — switch to a type-only import.
Apply this minimal diff:
-// eslint-disable-next-line no-restricted-imports -import { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql' +// eslint-disable-next-line no-restricted-imports +import type { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql'Optional follow-up (separate change): create frontend/src/types/graphql.ts
// re-export types to avoid importing from __generated__ all over export type { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql'Then update this file:
-// eslint-disable-next-line no-restricted-imports -import type { ExperienceLevelEnum, ProgramStatusEnum } from './__generated__/graphql' +import type { ExperienceLevelEnum, ProgramStatusEnum } from 'types/graphql'frontend/src/types/issue.ts (1)
7-7
: Immutable labels: consider ReadonlyArrayMinor: if labels are not mutated, convey intent and help the compiler.
- labels?: string[] + labels?: ReadonlyArray<string>frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (1)
56-56
: Date casting should be verified for safety.The cast of
createdAt
to string in the date formatting should be verified against the actual data type returned by the GraphQL query, especially given the scalar mapping configuration.Based on the codegen configuration that maps Date/DateTime to
'string | number'
, this cast might be unsafe if the backend returns epoch seconds:- new Date(m.createdAt as string).toLocaleString('default', { + new Date(typeof m.createdAt === 'number' ? m.createdAt * 1000 : m.createdAt as string).toLocaleString('default', {frontend/src/utils/structuredData.ts (2)
4-15
: formatISODate: undefined and ms/sec handling; avoid throwing.If createdAt/updatedAt are absent or already in milliseconds, current logic can throw or mis-convert. Make the helper accept undefined and auto-detect seconds vs milliseconds; return undefined instead of throwing so callers can omit the field.
Apply this diff:
-export const formatISODate = (input: number | string): string => { - const date = - typeof input === 'number' - ? new Date(input * 1000) // Unix timestamp in seconds - : new Date(input) // ISO date string - - if (isNaN(date.getTime())) { - throw new Error('Invalid date') - } - - return date.toISOString() -} +export const formatISODate = (input: number | string | undefined): string | undefined => { + if (input == null) return undefined + if (typeof input === 'number') { + // Heuristic: > 1e12 likely ms; else seconds. + const ms = input > 1_000_000_000_000 ? input : input * 1000 + const d = new Date(ms) + return isNaN(d.getTime()) ? undefined : d.toISOString() + } + const d = new Date(input) + return isNaN(d.getTime()) ? undefined : d.toISOString() +}
58-59
: Avoid emitting sameAs: [undefined].Only include sameAs when URL exists.
Apply this diff:
- sameAs: [user.url], + ...(user.url && { sameAs: [user.url] }),frontend/src/app/members/[memberKey]/page.tsx (1)
29-29
: Initialize user state to null to avoid triple-state (undefined | null | User).Current call without an initial value makes state possibly undefined and complicates checks.
Apply this diff:
- const [user, setUser] = useState<User | null>() + const [user, setUser] = useState<User | null>(null)frontend/src/app/settings/api-keys/page.tsx (3)
20-26
: Use generated GraphQL types; drop legacy ApiKey type to prevent drift.Stay consistent with codegen outputs to avoid mismatches and unsafe casts.
Apply this diff:
import { CreateApiKeyDocument, GetApiKeysDocument, RevokeApiKeyDocument, } from 'types/__generated__/apiKeyQueries.generated' -import { ApiKey } from 'types/apiKey' +import type { GetApiKeysQuery } from 'types/__generated__/apiKeyQueries.generated'
37-37
: Key-to-revoke state should use generated item type.Aligns state with the query result shape.
Apply this diff:
- const [keyToRevoke, setKeyToRevoke] = useState<ApiKey | null>(null) + const [keyToRevoke, setKeyToRevoke] = useState<GetApiKeysQuery['apiKeys'][0] | null>(null)
242-252
: Drop explicit ApiKey annotation and unsafe casts; rely on codegen types.Remove the manual type on map and string casts—codegen already types uuid/createdAt/expiresAt as strings.
Apply this diff:
- {data.apiKeys.map((key: ApiKey) => ( + {data.apiKeys.map((key) => ( <tr - key={key.uuid as string} + key={key.uuid} className="border-b border-b-gray-200 dark:border-b-gray-700" > <td className="py-3">{key.name}</td> - <td className="py-3 font-mono text-sm">{key.uuid as string}</td> - <td className="py-3">{format(new Date(key.createdAt as string), 'PP')}</td> + <td className="py-3 font-mono text-sm">{key.uuid}</td> + <td className="py-3">{format(new Date(key.createdAt), 'PP')}</td> <td className="py-3"> - {key.expiresAt ? format(new Date(key.expiresAt as string), 'PP') : 'Never'} + {key.expiresAt ? format(new Date(key.expiresAt), 'PP') : 'Never'} </td>frontend/src/app/snapshots/[id]/page.tsx (3)
138-142
: Use numeric zIndex.Style props expect a number;
'0'
is a string.Apply:
- style={{ height: '400px', width: '100%', zIndex: '0' }} + style={{ height: '400px', width: '100%', zIndex: 0 }}
145-147
: Remove stray text node.The lone
{' '}
is noise and can trip linters.Apply:
- {' '} {snapshot.newChapters.filter((chapter) => chapter.isActive).map(renderChapterCard)}
12-12
: Import SnapshotDetails as a typetsconfig.json (frontend/tsconfig.json) does not set preserveValueImports/verbatimModuleSyntax/importsNotUsedAsValues — still use an explicit type import to avoid runtime import issues.
File: frontend/src/app/snapshots/[id]/page.tsx
-import { SnapshotDetails } from 'types/snapshot' +import type { SnapshotDetails } from 'types/snapshot'frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (3)
13-13
: Align local state types to generated query types to avoid drift.Prefer types derived from GetProgramAndModulesQuery so state matches the actual selection set.
-import { Program, Module } from 'types/mentorship' +import type { GetProgramAndModulesQuery } from 'types/__generated__/graphql'And later:
-const [program, setProgram] = useState<Program | null>(null) -const [modules, setModules] = useState<Module[]>([]) +type ProgramT = NonNullable<GetProgramAndModulesQuery['getProgram']> +type ModuleT = GetProgramAndModulesQuery['getProgramModules'][number] +const [program, setProgram] = useState<ProgramT | null>(null) +const [modules, setModules] = useState<ModuleT[]>([])
31-33
: Resolve the TODO or remove it to keep migration clean.If you plan to rely on refetchQueries, drop the TODO; otherwise implement an update/optimisticResponse for cache sync.
I can wire an optimistic response for status flips if you want a follow‑up patch.
2-2
: Prefer root Apollo hooks export ('@apollo/client') instead of '@apollo/client/react'.Project uses @apollo/[email protected] (frontend/pnpm-lock.yaml); change the import in frontend/src/app/my/mentorship/programs/[programKey]/page.tsx from
import { useMutation, useQuery } from '@apollo/client/react'
to
import { useMutation, useQuery } from '@apollo/client'.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (14)
frontend/pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
frontend/src/types/__generated__/apiKeyQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/graphql.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/homeQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/moduleMutations.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/moduleQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/organizationQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/programsMutations.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/programsQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/projectQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/repositoryQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/snapshotQueries.generated.ts
is excluded by!**/__generated__/**
frontend/src/types/__generated__/userQueries.generated.ts
is excluded by!**/__generated__/**
📒 Files selected for processing (40)
frontend/graphql-codegen.ts
(2 hunks)frontend/package.json
(2 hunks)frontend/src/app/about/page.tsx
(4 hunks)frontend/src/app/chapters/[chapterKey]/page.tsx
(1 hunks)frontend/src/app/committees/[committeeKey]/page.tsx
(3 hunks)frontend/src/app/members/[memberKey]/page.tsx
(3 hunks)frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(2 hunks)frontend/src/app/mentorship/programs/[programKey]/page.tsx
(3 hunks)frontend/src/app/mentorship/programs/page.tsx
(2 hunks)frontend/src/app/my/mentorship/page.tsx
(3 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
(2 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(2 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
(2 hunks)frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
(4 hunks)frontend/src/app/my/mentorship/programs/create/page.tsx
(2 hunks)frontend/src/app/organizations/[organizationKey]/layout.tsx
(1 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx
(2 hunks)frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx
(2 hunks)frontend/src/app/page.tsx
(3 hunks)frontend/src/app/projects/[projectKey]/layout.tsx
(2 hunks)frontend/src/app/projects/[projectKey]/page.tsx
(2 hunks)frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
(4 hunks)frontend/src/app/projects/dashboard/metrics/page.tsx
(6 hunks)frontend/src/app/projects/dashboard/page.tsx
(3 hunks)frontend/src/app/settings/api-keys/page.tsx
(6 hunks)frontend/src/app/snapshots/[id]/layout.tsx
(2 hunks)frontend/src/app/snapshots/[id]/page.tsx
(4 hunks)frontend/src/app/snapshots/page.tsx
(2 hunks)frontend/src/types/chapter.ts
(1 hunks)frontend/src/types/committee.ts
(1 hunks)frontend/src/types/healthMetrics.ts
(1 hunks)frontend/src/types/issue.ts
(1 hunks)frontend/src/types/mentorship.ts
(3 hunks)frontend/src/types/milestone.ts
(1 hunks)frontend/src/types/organization.ts
(1 hunks)frontend/src/types/project.ts
(1 hunks)frontend/src/types/pullRequest.ts
(1 hunks)frontend/src/types/release.ts
(1 hunks)frontend/src/types/user.ts
(1 hunks)frontend/src/utils/structuredData.ts
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- frontend/src/app/organizations/[organizationKey]/layout.tsx
🚧 Files skipped from review as they are similar to previous changes (15)
- frontend/src/app/mentorship/programs/[programKey]/page.tsx
- frontend/src/app/projects/dashboard/page.tsx
- frontend/package.json
- frontend/src/app/my/mentorship/programs/create/page.tsx
- frontend/src/app/committees/[committeeKey]/page.tsx
- frontend/src/app/about/page.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
- frontend/src/app/projects/[projectKey]/page.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
- frontend/src/app/snapshots/page.tsx
- frontend/src/app/projects/dashboard/metrics/page.tsx
- frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/page.tsx
- frontend/src/app/chapters/[chapterKey]/page.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
- frontend/src/app/page.tsx
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/mentorship/programs/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/src/app/mentorship/programs/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.
Applied to files:
frontend/src/app/mentorship/programs/page.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/src/app/mentorship/programs/page.tsx
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
frontend/src/types/healthMetrics.ts
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
frontend/src/types/healthMetrics.ts
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Applied to files:
frontend/src/app/snapshots/[id]/page.tsx
frontend/src/app/my/mentorship/page.tsx
📚 Learning: 2025-09-21T11:34:33.377Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
Applied to files:
frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.
Applied to files:
frontend/src/app/members/[memberKey]/page.tsx
frontend/src/app/my/mentorship/page.tsx
🧬 Code graph analysis (21)
frontend/src/types/pullRequest.ts (1)
frontend/src/types/user.ts (1)
User
(10-32)
frontend/src/types/committee.ts (1)
frontend/src/types/contributor.ts (1)
Contributor
(1-8)
frontend/src/app/projects/[projectKey]/layout.tsx (1)
frontend/src/types/__generated__/projectQueries.generated.ts (1)
GetProjectMetadataDocument
(37-37)
frontend/src/app/mentorship/programs/page.tsx (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
ProgramStatusEnum
(22-27)
frontend/src/types/chapter.ts (1)
frontend/src/types/contributor.ts (1)
Contributor
(1-8)
frontend/src/types/milestone.ts (1)
frontend/src/types/user.ts (1)
User
(10-32)
frontend/src/types/user.ts (4)
backend/apps/github/models/user.py (1)
User
(19-159)frontend/src/types/issue.ts (1)
Issue
(3-19)frontend/src/types/release.ts (1)
Release
(3-13)frontend/src/types/project.ts (1)
RepositoryCardProps
(52-62)
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
frontend/src/types/__generated__/repositoryQueries.generated.ts (1)
GetRepositoryMetadataDocument
(22-22)
frontend/src/app/settings/api-keys/page.tsx (1)
frontend/src/types/__generated__/apiKeyQueries.generated.ts (3)
GetApiKeysDocument
(25-25)CreateApiKeyDocument
(26-26)RevokeApiKeyDocument
(27-27)
frontend/src/app/snapshots/[id]/layout.tsx (1)
frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
GetSnapshotDetailsMetadataDocument
(25-25)
frontend/src/types/release.ts (1)
frontend/src/types/user.ts (1)
User
(10-32)
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (2)
frontend/src/types/healthMetrics.ts (1)
HealthMetricsProps
(26-54)frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (1)
GetProjectHealthMetricsDetailsDocument
(28-28)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (3)
frontend/src/app/global-error.tsx (1)
handleAppError
(66-86)frontend/src/types/__generated__/programsQueries.generated.ts (1)
GetProgramAndModulesDocument
(37-37)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/types/mentorship.ts (2)
backend/apps/mentorship/api/internal/nodes/enum.py (2)
ProgramStatusEnum
(22-27)ExperienceLevelEnum
(12-18)frontend/src/types/contributor.ts (1)
Contributor
(1-8)
frontend/src/app/snapshots/[id]/page.tsx (3)
frontend/src/types/snapshot.ts (1)
SnapshotDetails
(5-13)frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
GetSnapshotDetailsDocument
(24-24)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)
frontend/src/types/project.ts (2)
frontend/src/types/healthMetrics.ts (1)
HealthMetricsProps
(26-54)frontend/src/types/contributor.ts (1)
Contributor
(1-8)
frontend/src/types/issue.ts (1)
frontend/src/types/user.ts (2)
User
(10-32)RepositoryDetails
(5-8)
frontend/src/app/members/[memberKey]/page.tsx (2)
frontend/src/types/user.ts (1)
User
(10-32)frontend/src/types/__generated__/userQueries.generated.ts (1)
GetUserDataDocument
(27-27)
frontend/src/app/my/mentorship/page.tsx (1)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
GetMyProgramsDocument
(35-35)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (4)
frontend/src/types/mentorship.ts (1)
Module
(42-54)frontend/src/types/__generated__/moduleQueries.generated.ts (1)
GetProgramAdminsAndModulesDocument
(30-30)frontend/src/utils/dateFormatter.ts (1)
formatDate
(1-20)frontend/src/components/ModuleCard.tsx (1)
getSimpleDuration
(98-110)
frontend/src/utils/structuredData.ts (2)
frontend/src/types/user.ts (1)
User
(10-32)frontend/src/types/profilePageStructuredData.ts (1)
ProfilePageStructuredData
(1-31)
🔇 Additional comments (34)
frontend/src/types/chapter.ts (2)
3-3
: No generated Chapter type found under generated; verify codegen outputs or rename local typerg shows a local
export type Chapter = …
in frontend/src/types/chapter.ts and no matches under generated; confirm whether codegen emits aChapter
type elsewhere—if it does, re‑export the generated type; otherwise rename this local type (e.g., DomainChapter) or mark it @deprecated to avoid confusion.
5-5
: createdAt/updatedAt OK as number for Chapter — no change required.Generated types declare ChapterNode.createdAt/updatedAt as number (e.g. frontend/src/types/generated/snapshotQueries.generated.ts and frontend/src/types/generated/homeQueries.generated.ts), so keep createdAt?: number in frontend/src/types/chapter.ts unless you change the GraphQL scalar mapping.
frontend/src/types/committee.ts (1)
8-8
: Require Committee.key or justify optionality
- File: frontend/src/types/committee.ts — don't weaken the primary type. If production always provides the identifier, revert:
- key?: string + key: string
- If some payloads legitimately omit it, introduce a separate partial/preview type (e.g., CommitteePreview) instead of loosening the main Committee type.
- Avoid spreading Committee into JSX (React reserves
key
); if spreads are needed, rename the field (e.g.,committeeKey
) in a follow‑up.frontend/src/types/healthMetrics.ts (1)
26-54
: Optionalizing most fields — confirm null vs undefined semantics and downstream usageFindings: frontend/tsconfig.json sets "strict": false and does not set strictNullChecks or exactOptionalPropertyTypes. I could not locate GraphQL codegen config or package.json in the repo root.
- Verify GraphQL codegen nullability (avoidOptionals / maybeValue / nonOptionalTypename) — does codegen emit null (Maybe) or omit fields (undefined)?
- Scan consumers for arithmetic/sorting/filters and React key usage on newly-optional fields (ageDays, lastCommitDays, score, projectKey, starsCount, forksCount, openIssuesCount, totalIssuesCount) and add runtime guards or adapt types.
- If aligning types, match codegen semantics (e.g., export type Maybe = T | null) instead of mixing null and undefined.
frontend/src/types/mentorship.ts (1)
52-53
: Guard optional domains/tags at call sites or default to []domains/tags are now optional — ensure no code does array ops on undefined. Review/adjust these call-sites (guard or default to []):
- frontend/src/app/mentorship/programs/[programKey]/page.tsx — tags={program.tags} (line ~86), domains={program.domains} (line ~87)
- frontend/src/app/my/mentorship/programs/[programKey]/page.tsx — tags={program.tags} (149), domains={program.domains} (150)
- frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx — tags={module.tags} (65), domains={module.domains} (66)
- frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx — tags={module.tags} (65), domains={module.domains} (66)
- frontend/src/components/ModuleForm.tsx — value={formData.domains} (168), value={formData.tags} (182)
- frontend/src/components/ProgramForm.tsx — value={formData.tags} (166), value={formData.domains} (180)
- create/edit pages use parseCommaSeparated(formData.*) — confirm parseCommaSeparated handles undefined (create/page.tsx, modules/create/page.tsx, edit/page.tsx).
- Already guarded (OK): frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx — (program.tags || []).join(', '), (program.domains || []).join(', ') (lines ~79–80); modules edit page has similar guards.
Fix patterns: (x ?? []).map(...), x?.length ?? 0, or default array when forwarding props (e.g., tags={program.tags ?? []}).
frontend/graphql-codegen.ts (2)
34-34
: Scalar type configuration aligned with Apollo Client v4 migration.The change from
'unknown'
to'any'
for the default scalar type follows a practical approach for the Apollo Client v4 migration, where unknown scalar types need to be handled more flexibly during the transition phase.
53-60
: Good scalar mapping for Date and DateTime fields.The scalar configuration correctly maps GraphQL Date and DateTime scalars to
'string | number'
, which aligns with the broader migration pattern seen across the codebase where date fields are being cast to strings for formatting functions.frontend/src/app/mentorship/programs/page.tsx (2)
5-5
: Consistent enum usage adoption.Good migration from string literals to the generated
ProgramStatusEnum
- this improves type safety and aligns with the broader pattern of using generated GraphQL types throughout the codebase.
55-56
: Verify enum value consistency with backendfrontend/src/app/mentorship/programs/page.tsx (lines 55–56):
{programs && programs.filter((p) => p.status === ProgramStatusEnum.Published).map(renderProgramCard)}
ast-grep returned no matches for ProgramStatusEnum or generated GraphQL ProgramStatusEnum in the repo. Confirm that ProgramStatusEnum.Published serializes to the backend value 'published'; if it does not, replace with the correct string or update the enum mapping.
frontend/src/app/organizations/[organizationKey]/repositories/[repositoryKey]/layout.tsx (1)
4-4
: Successfully migrated to generated GraphQL document.The import and usage update from
GET_REPOSITORY_METADATA
toGetRepositoryMetadataDocument
correctly follows the Apollo Client v4 migration pattern with generated document nodes.Also applies to: 17-17
frontend/src/app/projects/[projectKey]/layout.tsx (1)
4-4
: Clean migration to generated GraphQL documents.The replacement of
GET_PROJECT_METADATA
withGetProjectMetadataDocument
is consistent with the project-wide migration to generated GraphQL types and maintains the same functional behavior.Also applies to: 16-16
frontend/src/types/organization.ts (1)
2-16
: Type flexibility improvements for GraphQL integration.Making most Organization properties optional (except
login
) aligns well with GraphQL's nullable field handling and provides better compatibility with generated types. This change supports partial data scenarios common in GraphQL queries.frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (4)
2-2
: Apollo v4 React hooks import correctly updated.The import path change to
@apollo/client/react
aligns with Apollo Client v4's new entrypoint structure.
7-7
: Generated GraphQL document migration completed.The migration from
GET_PROGRAM_ADMINS_AND_MODULES
toGetProgramAdminsAndModulesDocument
follows the established pattern throughout the codebase.Also applies to: 20-20
15-15
: Good route parameter typing improvement.Adding explicit types to
useParams<{ programKey: string; moduleKey: string }>()
improves type safety.
52-57
: Date handling casts are fragile.The type casting of
startedAt
/endedAt
tostring
is potentially unsafe if the backend returns epoch seconds. Based on the codegen configuration showing Date/DateTime scalars mapped to'string | number'
, these fields could be numbers.frontend/src/app/my/mentorship/page.tsx (2)
2-2
: **Apollo Client v4 React hooks import is correct.**The import from@apollo/client/react
is correct for Apollo Client v4. React hooks now live in the @apollo/client/react entrypoint, allowing developers to use Apollo Client with any JavaScript framework without React dependencies.
11-11
: Successful migration to generated GraphQL document.The replacement of
GET_MY_PROGRAMS
withGetMyProgramsDocument
follows the established pattern throughout the codebase and maintains the same functionality.Also applies to: 56-56
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (3)
2-2
: Apollo Client v4 React hooks import is correct.The updated import path aligns with Apollo Client v4's new module structure.
17-17
: GraphQL document migration completed successfully.The replacement of
GET_PROJECT_HEALTH_METRICS_DETAILS
withGetProjectHealthMetricsDetailsDocument
follows the established migration pattern.Also applies to: 34-34
27-27
: Good route parameter typing enhancement.Adding explicit typing to
useParams<{ projectKey: string }>()
improves type safety.frontend/src/types/milestone.ts (1)
4-12
: Optionalizing many Milestone fields—verify downstream null-safety.author, body, closedIssuesCount, createdAt, openIssuesCount, repositoryName, and state are now optional. Please double-check all consumers (rendering, sorting, formatting) for unchecked property access and add guards where needed to avoid runtime undefined issues.
frontend/src/types/user.ts (1)
14-31
: createdAt/updatedAt widened to number | string and many fields optional—audit call sites.Ensure all usages (e.g., date formatters, comparisons, arithmetic) handle both string and number, and that optional fields are null‑safe. This change can silently break UI if any code assumed required numbers.
frontend/src/types/release.ts (1)
4-5
: LGTM: author/isPreRelease made optional to match varying data.Change aligns with broader optionality adjustments and should be safe with null‑checks in consumers.
frontend/src/types/project.ts (1)
19-39
: Project surface relaxed—verify consumers for null/shape assumptions.Many fields are now optional (and organizations is a string). Please sanity‑check renderers, filters, and sorters that previously assumed presence or array/object shapes.
frontend/src/app/snapshots/[id]/page.tsx (5)
9-9
: Good move to codegen documents.Using
GetSnapshotDetailsDocument
aligns with Apollo v4 + codegen.
22-22
: CorrectuseParams
typing.Explicit param typing is appropriate here.
27-29
: useQuery usage matches Apollo v4 expectations.Document + variables wiring looks correct.
124-125
: Don’t force date scalars to string; pass through as-is.
formatDate
already acceptsnumber | string
. The casts are unnecessary and can hide type issues.Apply:
- {formatDate(snapshot.startAt as string)} - {formatDate(snapshot.endAt as string)} + {formatDate(snapshot.startAt)} - {formatDate(snapshot.endAt)}
2-2
: Approve: Apollo hook imports are consistent in frontend/src.
Search shows React hook imports use '@apollo/client/react' across frontend/src; remaining '@apollo/client' usages are server-side (ApolloClient/gql) and expected.frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)
10-11
: LGTM: generated enums and documents are correctly adopted.Using ProgramStatusEnum and the typed GetProgramAndModulesDocument aligns with Apollo v4 + codegen best practices.
36-44
: LGTM: useQuery with typed document + skip flag.Typed variables and notifyOnNetworkStatusChange fit the migration; skip guards undefined params.
79-79
: Apollo v4 note: refetchQueries are not awaited; verify UX expectations.If the toast/navigation should occur after data is refreshed, explicitly await refetch() or manually await the returned refs; otherwise current behavior is fine.
Minimal alternative:
- await updateProgram({ + await updateProgram({ variables: { /* ... */ }, - refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }], + refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }], }) + await refetch()
130-133
: Harden date formatting and humanize enum; avoid unsafe casts.
- Casting to string can yield "Invalid date" for numeric epochs and can still throw in formatDate. Pass number|string with null guards.
- ProgramStatusEnum values are typically UPPER_CASE; upperFirst keeps them shouting. Lowercase then title‑case for display.
- const programDetails = [ - { label: 'Status', value: upperFirst(program.status) }, - { label: 'Start Date', value: formatDate(program.startedAt as string) }, - { label: 'End Date', value: formatDate(program.endedAt as string) }, + const programDetails = [ + { label: 'Status', value: upperFirst(program.status.toLowerCase()) }, + { label: 'Start Date', value: program.startedAt ? formatDate(program.startedAt as number | string) : 'N/A' }, + { label: 'End Date', value: program.endedAt ? formatDate(program.endedAt as number | string) : 'N/A' },Additionally (elsewhere in this file), for the toast:
- title: `Program status updated to ${upperFirst(newStatus)}`, + title: `Program status updated to ${upperFirst(newStatus.toLowerCase())}`,Optional follow‑up: map Date/DateTime scalars to string in codegen.yml to avoid unknown and casts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
frontend/src/app/snapshots/[id]/page.tsx (3)
2-2
: Confirm import path choice for Apollo v4 hooks.Both '@apollo/client' and '@apollo/client/react' work in v4. The docs generally show root imports; mixing entrypoints across files can increase bundle duplication. If consistency is a goal, consider the root import.
- import { useQuery } from '@apollo/client/react' + import { useQuery } from '@apollo/client'
12-12
: Make SnapshotDetails a type-only import.Avoids creating a runtime import for a type-only symbol.
- import { SnapshotDetails } from 'types/snapshot' + import type { SnapshotDetails } from 'types/snapshot'
145-146
: Remove stray whitespace text node in JSX.The {' '} fragment adds an unnecessary text child and may trip linters.
- {' '} {snapshot.newChapters.filter((chapter) => chapter.isActive).map(renderChapterCard)}
frontend/src/app/mentorship/programs/[programKey]/page.tsx (2)
8-8
: Use type‑only import to avoid bundling the types modulePrevents accidental runtime import and improves tree‑shaking.
-import { Program, Module } from 'types/mentorship' +import type { Program, Module } from 'types/mentorship'
14-14
: Prefer typed useParams over assertion (match modules page)Consistent, safer typing without a cast.
-const { programKey } = useParams() as { programKey: string } +const { programKey } = useParams<{ programKey: string }>()frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2)
8-8
: Type‑only import for ModuleAvoids emitting a runtime import.
-import { Module } from 'types/mentorship' +import type { Module } from 'types/mentorship'
17-17
: Optional: type admins stateIf DetailsCard expects a Contributor[] (likely), type it to preserve inference.
-const [admins, setAdmins] = useState(null) +import type { Contributor } from 'types/mentorship' +const [admins, setAdmins] = useState<Contributor[] | null>(null)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(1 hunks)frontend/src/app/mentorship/programs/[programKey]/page.tsx
(2 hunks)frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
(2 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
(1 hunks)frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
(3 hunks)frontend/src/app/settings/api-keys/page.tsx
(5 hunks)frontend/src/app/snapshots/[id]/page.tsx
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/src/app/settings/api-keys/page.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Applied to files:
frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-09-21T11:34:33.377Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
Applied to files:
frontend/src/app/snapshots/[id]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/app/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx
🧬 Code graph analysis (4)
frontend/src/app/snapshots/[id]/page.tsx (2)
frontend/src/types/snapshot.ts (1)
SnapshotDetails
(5-13)frontend/src/types/__generated__/snapshotQueries.generated.ts (1)
GetSnapshotDetailsDocument
(24-24)
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2)
frontend/src/types/mentorship.ts (1)
Module
(42-54)frontend/src/types/__generated__/moduleQueries.generated.ts (1)
GetProgramAdminsAndModulesDocument
(30-30)
frontend/src/app/mentorship/programs/[programKey]/page.tsx (1)
frontend/src/types/__generated__/programsQueries.generated.ts (1)
GetProgramAndModulesDocument
(37-37)
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (2)
frontend/src/types/healthMetrics.ts (1)
HealthMetricsProps
(26-54)frontend/src/types/__generated__/projectsHealthDashboardQueries.generated.ts (1)
GetProjectHealthMetricsDetailsDocument
(28-28)
🔇 Additional comments (12)
frontend/src/app/snapshots/[id]/page.tsx (3)
9-9
: Good: switched to generated Document for typed queries.Using GetSnapshotDetailsDocument aligns with Apollo v4 + codegen best practices.
22-22
: Typed route params look correct.useParams<{ id: string }>() is appropriate for /snapshots/[id].
27-29
: LGTM on useQuery with generated document.Inference from the typed DocumentNode obviates generics; variables usage is correct.
frontend/src/app/mentorship/programs/[programKey]/page.tsx (2)
2-2
: Apollo v4 hook import — LGTMCorrectly importing React hooks from @apollo/client/react.
7-7
: Switch to generated document — LGTM; Date/DateTime scalars emitted as any in generated types (fix required)graphql-codegen.ts maps Date/DateTime -> 'string | number' for ./src/types/generated/graphql.ts, but operation-generated files still emit startedAt/endedAt as any.
Affected: frontend/src/types/generated/programsQueries.generated.ts, programsMutations.generated.ts, moduleQueries.generated.ts, moduleMutations.generated.ts.
Action: apply the scalars mapping to the operation outputs (or use a global mapping), re-run codegen, and verify startedAt/endedAt are typed 'string | number' (not any).frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (3)
2-2
: Apollo v4 hook import — LGTM
7-7
: Generated document + useQuery wiring — LGTMDocument and variables match the query signature.
Also applies to: 20-25
15-15
: Typed useParams — LGTMGood switch to generic typing for route params.
frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx (4)
27-27
: TypeduseParams
for App Router is spot on.Matches prior learning about extracting params in Next 13+. Thanks for keeping this scoped to migration.
34-36
:useQuery
with the generated document and inline variables — LGTM.Types are inferred from the DocumentNode; no generics needed.
17-17
: Approve — generated DocumentNode usage is correct; legacy constant only defined, not referenced elsewhereGetProjectHealthMetricsDetailsDocument is imported/used in frontend/src/app/projects/dashboard/metrics/[projectKey]/page.tsx; the legacy GET_PROJECT_HEALTH_METRICS_DETAILS only exists at frontend/src/server/queries/projectsHealthDashboardQueries.ts and has no other references — safe to remove the legacy constant if you want to clean up.
2-2
: Switch to@apollo/client/react
— app code migrated; tests need updatesApp pages/hooks/components now import from
@apollo/client/react
(v4 migration looks correct). Server-side modules (frontend/src/server/** and utils/helpers/apolloClient.ts) correctly keep top-level@apollo/client
for non‑React exports (ApolloClient, gql, link/context).
Many unit tests still import hooks from the top-level@apollo/client
(e.g. frontend/tests/unit/pages/About.test.tsx, ProjectDetails.test.tsx, CreateModule.test.tsx, EditModule.test.tsx, ApiKeysPage.test.tsx and other files under frontend/tests/unit/pages/**) — update those tests or their jest.mock targets to import/mock@apollo/client/react
so hooks resolve to the React entrypoint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
frontend/__tests__/unit/pages/About.test.tsx (2)
532-541
: Do not compare to legacy GET_ if the component uses generated documents.*Align this condition with operation-name matching or generated documents.
116-133
: Fix fragile useQuery mock in About.test.tsx — match generated documents or operation namefrontend/src/app/about/page.tsx uses generated documents (GetProjectMetadataDocument, GetTopContributorsDocument, GetLeaderDataDocument); the test's mock in frontend/tests/unit/pages/About.test.tsx (lines 116–133) currently compares against GET_* and will break. Update the mock to either match the operation name (document.definitions[0].name.value) or import and compare against the same generated Document objects used by the component.
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (1)
67-76
: Replace legacy mutation identity checks with generated DocumentNodesfrontend/src/app/settings/api-keys/page.tsx imports CreateApiKeyDocument and RevokeApiKeyDocument; the test currently compares against CREATE_API_KEY/REVOKE_API_KEY and will break. Update the mock in frontend/tests/unit/pages/ApiKeysPage.test.tsx (lines 67–76) to compare against CreateApiKeyDocument / RevokeApiKeyDocument (or match by operation name) instead of the old constants.
🧹 Nitpick comments (31)
frontend/__tests__/unit/utils/structuredData.test.ts (3)
1-1
: Use type‑only import for clarity and to avoid accidental value import.
Switch toimport type
sinceUser
is a type alias.-import { User } from 'types/user' +import type { User } from 'types/user'
10-19
: Fix mockUser date fields to match expected normalized ISO timestamps.
Your expectations use epoch+2020s and +2021s (1970-01-01T00:33:40Z / 00:33:41Z). Update the fixture to numbers to avoid mismatch.- createdAt: '2020-01-01T00:00:00Z', + createdAt: 2020, email: '[email protected]', @@ - updatedAt: '2021-02-03T00:00:00Z', + updatedAt: 2021,If
formatISODate
treats numbers as seconds (not ms), this will pass. If it expects ms, use2000
and2001
seconds × 1000 respectively. Please run the unit tests to confirm.
61-72
: Fix minimalUser date fields to match expected normalization.
Mirror the same numeric dates so the second test remains consistent.- createdAt: '2020-01-01T00:00:00Z', + createdAt: 2020, @@ - updatedAt: '2021-02-03T00:00:00Z', + updatedAt: 2021,frontend/__tests__/unit/pages/MyMentorship.test.tsx (1)
33-33
: Casting via unknown works; optionally prefer jest.MockedFunction typing.
Keeps types tighter without double cast.Apply if desired:
-const mockUseQuery = useQuery as unknown as jest.Mock +const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery>If this trips on generics/overloads in your tsconfig, keep the current cast to stay unblocked for migration.
frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (1)
64-69
: Optional: DRY the useQuery mock castAvoid repeating
(useQuery as unknown as jest.Mock)
by defining a single typed alias and reusing it.Add once near the imports:
const mockedUseQuery = useQuery as unknown as jest.MockThen update the call sites:
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + mockedUseQuery.mockReturnValue({(Apply the same replacement at Lines 75, 88, and 177.)
Also applies to: 75-80, 88-93, 177-182
frontend/__tests__/unit/pages/ProjectDetails.test.tsx (2)
46-46
: Remove repetitive double-casts by aliasing the mock once.Define a typed alias once and use it, improving readability and avoiding repeated casts.
Apply this diff for the repeated usage lines:
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + useQueryMock.mockReturnValue({Add this alias near the top (after imports):
const useQueryMock = useQuery as unknown as jest.MockAlso applies to: 58-58, 72-72, 89-89, 156-156, 171-171, 197-197, 218-218, 264-264, 280-280
58-61
: Set loading explicitly for the loading-state test.To avoid relying on component internals, return loading: true when asserting the spinner.
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ - data: null, - error: null, - }) + ;(useQuery as unknown as jest.Mock).mockReturnValue({ + data: null, + loading: true, + error: null, + })frontend/__tests__/unit/pages/EditModule.test.tsx (1)
26-29
: Preserve all exports when mocking @apollo/client.Spread the actual module to avoid unintentionally nulling other exports used transitively (e.g., gql, types, utility fns).
-jest.mock('@apollo/client', () => ({ - useApolloClient: jest.fn(), - gql: jest.requireActual('@apollo/client').gql, -})) +jest.mock('@apollo/client', () => ({ + ...jest.requireActual('@apollo/client'), + useApolloClient: jest.fn(), +}))frontend/__tests__/unit/pages/ModuleDetails.test.tsx (1)
34-34
: Casting approach is fine; optional stronger typing.Current cast is acceptable. Optionally, tighten typing if TS allows:
- const mockUseQuery = useQuery as unknown as jest.Mock + const mockUseQuery = useQuery as unknown as jest.MockedFunction<typeof useQuery>frontend/__tests__/unit/pages/SnapshotDetails.test.tsx (4)
8-11
: Mocking the React entrypoint looks good; reduce repeated casts.To avoid repeating
(useQuery as unknown as jest.Mock)
everywhere, create a single alias:Add near the imports:
const mockUseQuery = useQuery as unknown as jest.MockThen replace usages with
mockUseQuery.mockReturnValue(...)
.
51-56
: Fix brittle loading assertion (getAllByAltText evaluated too early).
getAllByAltText
is called before thewaitFor
, so the test can fail immediately if the spinner renders asynchronously. UsefindAllByAltText
or move the query insidewaitFor
.Replace the assertion block with one of:
// Option A (preferred) const loadingSpinner = await screen.findAllByAltText('Loading indicator') expect(loadingSpinner.length).toBeGreaterThan(0) // Option B await waitFor(() => { expect(screen.getAllByAltText('Loading indicator').length).toBeGreaterThan(0) })
66-69
: Be explicit about loading=false to avoid flakiness.Not required, but adding
loading: false
makes intent clear if the component branches onloading
.
83-86
: Use an Error/ApolloError instance for the error shape.Right now
error
is{ error: Error }
. If the component checkserror.message
or expects an ApolloError, this may misbehave.Consider:
import { ApolloError } from '@apollo/client' // ... const mockError = new ApolloError({ errorMessage: 'GraphQL error' }) // ... (useQuery as unknown as jest.Mock).mockReturnValue({ data: null, error: mockError, })Or, minimally:
const mockError = new Error('GraphQL error') // ... error: mockError,frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx (1)
23-23
: DRY the repeated casts by defining a single typed mock for useQuery.Keeps casts in one place and improves readability.
Apply this change here:
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + mockedUseQuery.mockReturnValue({ data: mockProgramDetailsData, loading: false, refetch: jest.fn(), })Add this once near the imports (outside describe):
// after imports const mockedUseQuery = useQuery as unknown as jest.MockedFunction<typeof useQuery>frontend/__tests__/unit/pages/CreateProgram.test.tsx (1)
37-37
: Prefer typed mock helper over double-castUse jest.mocked for cleaner typing and readability.
- ;(useMutation as unknown as jest.Mock).mockReturnValue([mockCreateProgram, { loading: false }]) + jest.mocked(useMutation).mockReturnValue([mockCreateProgram, { loading: false }])frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx (1)
34-34
: Prefer precise typing overunknown as jest.Mock
.Use
jest.MockedFunction
for safer typings.Apply:
- const mockUseQuery = useQuery as unknown as jest.Mock + const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery>frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (1)
39-49
: Fix potential flakiness in loading-spinner assertion.Capturing elements before waiting can throw; use findAllBy* or assert inside waitFor.
Apply:
- const loadingSpinner = screen.getAllByAltText('Loading indicator') - await waitFor(() => { - expect(loadingSpinner.length).toBeGreaterThan(0) - }) + const loadingSpinner = await screen.findAllByAltText('Loading indicator') + expect(loadingSpinner.length).toBeGreaterThan(0)frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx (1)
40-44
: Avoid repeating casts; create a local mock alias for useQueryDefine a typed alias once and use it across tests to reduce noise.
Apply this diff to the current line:
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + ;mockUseQuery.mockReturnValue({Add this helper near the top (e.g., after the Apollo mock):
// place once near the mocks const mockUseQuery = useQuery as unknown as jest.Mock;frontend/__tests__/unit/pages/ProjectsHealthDashboardOverview.test.tsx (4)
35-37
: Mock error shape should be an Error (or ApolloError), not{ error: Error }
Use a direct Error to better match
useQuery
’serror
type and typical component checks.Apply:
-const mockError = { - error: new Error('GraphQL error'), -} +const mockError = new Error('GraphQL error')
41-46
: Casting works; optional: tighten typing with jest.MockedFunctionThis avoids double-casting to
unknown
and preservesuseQuery
’s call signature in mocks.Apply:
-;(useQuery as unknown as jest.Mock).mockReturnValue({ +;(useQuery as unknown as jest.MockedFunction<typeof useQuery>).mockReturnValue({ data: mockProjectsDashboardOverviewData, loading: false, error: null, })
52-56
: Same optional typing improvement for the loading state mockKeeps mocks consistently typed.
Apply:
-;(useQuery as unknown as jest.Mock).mockReturnValue({ +;(useQuery as unknown as jest.MockedFunction<typeof useQuery>).mockReturnValue({ data: null, loading: true, error: null, })
65-69
: Error state mock: align with direct Error instanceAfter adjusting
mockError
toError
, this will better reflect runtime behavior and component expectations.Optionally, use
.mockReturnValueOnce(...)
here to avoid cross-test bleed and rely on the default set inbeforeEach
.frontend/__tests__/unit/pages/UserDetails.test.tsx (1)
47-47
: Explicitly setloading
in alluseQuery
mocks; fix the loading-state test; optional typed helper to reduce casts.
- Some overrides omit
loading
. Be explicit to avoid fragile tests if component logic changes.- The “renders loading state” test should mock
loading: true
to reflect intent.Apply this diff for the loading test:
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ - data: null, - error: null, - }) + ;(useQuery as unknown as jest.Mock).mockReturnValue({ + data: null, + error: null, + loading: true, + })Apply this diff for the error test:
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ - data: null, - error: mockError, - }) + ;(useQuery as unknown as jest.Mock).mockReturnValue({ + data: null, + error: mockError, + loading: false, + })Optional (to reduce repeated casts):
// near the top of the file const mockedUseQuery = useQuery as unknown as jest.Mock; // usage mockedUseQuery.mockReturnValue({ data: ..., loading: false, error: null })Also applies to: 63-66, 76-83, 102-106, 123-127, 141-145, 159-163, 181-185, 205-209, 232-236, 255-258, 270-273, 339-343, 357-361, 376-380, 395-398, 412-416, 430-433, 452-456, 477-481, 492-495
frontend/__tests__/unit/pages/CreateModule.test.tsx (2)
59-66
: Prefer precise mock typing over unknown casts.Use jest.MockedFunction to avoid unsafe unknown as jest.Mock casts.
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + ;(useQuery as jest.MockedFunction<typeof useQuery>).mockReturnValue({ data: { getProgram: { admins: [{ login: 'admin-user' }], }, }, loading: false, })
67-70
: Type the mutation mock tuple precisely.This keeps the mock shape aligned with Apollo’s useMutation return type.
- ;(useMutation as unknown as jest.Mock).mockReturnValue([ + ;(useMutation as jest.MockedFunction<typeof useMutation>).mockReturnValue([ mockCreateModule.mockResolvedValue({}), { loading: false }, ])frontend/__tests__/unit/pages/RepositoryDetails.test.tsx (2)
49-60
: Setloading: true
and avoid pre-querying beforewaitFor
in the loading test.
Currently the mock omitsloading
, andgetAllByAltText
is called before the UI can update.Apply this diff to set loading explicitly:
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ - data: null, - error: null, - }) + ;(useQuery as unknown as jest.Mock).mockReturnValue({ + data: null, + loading: true, + error: null, + })Optionally, prefer an async query to reduce flakiness:
// Replace the getAllByAltText + waitFor with: const loadingSpinner = await screen.findAllByAltText('Loading indicator') expect(loadingSpinner.length).toBeGreaterThan(0)
82-86
: Use anError
(orApolloError
) directly for theerror
field.
Passing{ error: Error }
nests the error and may not match expectations.Apply this diff to pass the actual error:
- error: mockError, + error: mockError.error,Alternatively, construct an ApolloError if you want closer parity with v4:
import { ApolloError } from '@apollo/client' // ... error: new ApolloError({ networkError: new Error('GraphQL error') }),frontend/__tests__/unit/pages/EditProgram.test.tsx (1)
38-38
: Prefer typed mocking to avoidunknown as jest.Mock
castsUse
jest.mocked(useQuery)
orMockedFunction<typeof useQuery>
for type-safe returns.Example:
import type { MockedFunction } from 'jest-mock' const mockUseQuery = useQuery as unknown as MockedFunction<typeof useQuery> mockUseQuery.mockReturnValue({ loading: true } as any)frontend/__tests__/unit/pages/ProgramDetails.test.tsx (1)
23-27
: Avoid repeated casts by extracting a typed alias once.Define a single const and reuse to reduce repetition.
Apply this diff here and reuse the alias throughout the file:
describe('ProgramDetailsPage', () => { + const mockUseQuery = useQuery as unknown as jest.Mock beforeEach(() => { - ;(useQuery as unknown as jest.Mock).mockReturnValue({ + mockUseQuery.mockReturnValue({ data: mockProgramDetailsData, loading: false, refetch: jest.fn(), }) })Change other occurrences similarly (see Lines 35 and 48).
frontend/__tests__/unit/pages/Home.test.tsx (1)
73-78
: Extract a single mock alias to avoid repeated casts.Define mockUseQuery once and reuse.
Apply:
describe('Home', () => { let mockRouter: { push: jest.Mock } beforeEach(() => { - ;(useQuery as unknown as jest.Mock).mockReturnValue({ + const mockUseQuery = useQuery as unknown as jest.Mock + mockUseQuery.mockReturnValue({ data: mockGraphQLData, loading: false, error: null, })Replicate for other occurrences below (Lines 88, 113, 196, 248).
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (1)
51-52
: Typed aliases for mocks improve clarity.Good pattern; consider jest.MockedFunction<typeof useQuery/useMutation> for tighter typing (optional).
-const mockUseQuery = useQuery as unknown as jest.Mock -const mockUseMutation = useMutation as unknown as jest.Mock +const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery> +const mockUseMutation = useMutation as jest.MockedFunction<typeof useMutation>
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (27)
frontend/__tests__/unit/components/ProgramCard.test.tsx
(3 hunks)frontend/__tests__/unit/components/SingleModuleCard.test.tsx
(2 hunks)frontend/__tests__/unit/data/mockProgramData.ts
(2 hunks)frontend/__tests__/unit/pages/About.test.tsx
(14 hunks)frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
(3 hunks)frontend/__tests__/unit/pages/ChapterDetails.test.tsx
(7 hunks)frontend/__tests__/unit/pages/CommitteeDetails.test.tsx
(7 hunks)frontend/__tests__/unit/pages/CreateModule.test.tsx
(3 hunks)frontend/__tests__/unit/pages/CreateProgram.test.tsx
(2 hunks)frontend/__tests__/unit/pages/EditModule.test.tsx
(5 hunks)frontend/__tests__/unit/pages/EditProgram.test.tsx
(5 hunks)frontend/__tests__/unit/pages/Home.test.tsx
(7 hunks)frontend/__tests__/unit/pages/ModuleDetails.test.tsx
(3 hunks)frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx
(3 hunks)frontend/__tests__/unit/pages/MyMentorship.test.tsx
(2 hunks)frontend/__tests__/unit/pages/OrganizationDetails.test.tsx
(9 hunks)frontend/__tests__/unit/pages/ProgramDetails.test.tsx
(4 hunks)frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
(4 hunks)frontend/__tests__/unit/pages/ProjectDetails.test.tsx
(11 hunks)frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx
(5 hunks)frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
(5 hunks)frontend/__tests__/unit/pages/ProjectsHealthDashboardOverview.test.tsx
(4 hunks)frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
(7 hunks)frontend/__tests__/unit/pages/SnapshotDetails.test.tsx
(9 hunks)frontend/__tests__/unit/pages/Snapshots.test.tsx
(6 hunks)frontend/__tests__/unit/pages/UserDetails.test.tsx
(25 hunks)frontend/__tests__/unit/utils/structuredData.test.ts
(2 hunks)
✅ Files skipped from review due to trivial changes (3)
- frontend/tests/unit/pages/ChapterDetails.test.tsx
- frontend/tests/unit/pages/Snapshots.test.tsx
- frontend/tests/unit/pages/OrganizationDetails.test.tsx
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx
frontend/__tests__/unit/pages/EditProgram.test.tsx
frontend/__tests__/unit/pages/CreateModule.test.tsx
frontend/__tests__/unit/pages/ModuleDetails.test.tsx
frontend/__tests__/unit/pages/EditModule.test.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/__tests__/unit/pages/ProgramDetails.test.tsx
frontend/__tests__/unit/pages/EditProgram.test.tsx
frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/__tests__/unit/pages/ProgramDetails.test.tsx
frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.
Applied to files:
frontend/__tests__/unit/pages/ProgramDetails.test.tsx
frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.
Applied to files:
frontend/__tests__/unit/pages/ProgramDetails.test.tsx
frontend/__tests__/unit/pages/MyMentorship.test.tsx
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
frontend/__tests__/unit/pages/ProjectDetails.test.tsx
frontend/__tests__/unit/pages/CreateProgram.test.tsx
frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
frontend/__tests__/unit/pages/CommitteeDetails.test.tsx
📚 Learning: 2025-07-13T11:34:31.823Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:14-14
Timestamp: 2025-07-13T11:34:31.823Z
Learning: In the Next.js frontend mentorship application, there are two distinct types for authentication-related data: ExtendedSession for useSession hook (containing accessToken and user.login properties) and UserRolesData for useUserRoles hook (containing currentUserRoles.roles array). The correct access pattern for GitHub username is `(session as ExtendedSession)?.user?.login`.
Applied to files:
frontend/__tests__/unit/components/SingleModuleCard.test.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/__tests__/unit/pages/EditProgram.test.tsx
frontend/__tests__/unit/pages/CreateModule.test.tsx
frontend/__tests__/unit/pages/EditModule.test.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a length check before rendering HealthMetrics: `healthMetricsData.length > 0`. This ensures that when HealthMetrics is rendered, the data array has at least one element, making accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
📚 Learning: 2025-06-20T16:12:59.256Z
Learnt from: ahmedxgouda
PR: OWASP/Nest#1633
File: frontend/src/components/HealthMetrics.tsx:30-30
Timestamp: 2025-06-20T16:12:59.256Z
Learning: In the DetailsCard component (frontend/src/components/CardDetailsPage.tsx), there's a safety check that ensures HealthMetrics component is only rendered when healthMetricsData exists and has at least one element: `healthMetricsData && healthMetricsData.length > 0`. This makes accessing data[0] safe within the HealthMetrics component.
Applied to files:
frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx
🧬 Code graph analysis (4)
frontend/__tests__/unit/components/SingleModuleCard.test.tsx (1)
backend/apps/mentorship/api/internal/nodes/enum.py (2)
ProgramStatusEnum
(22-27)ExperienceLevelEnum
(12-18)
frontend/__tests__/unit/data/mockProgramData.ts (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
ProgramStatusEnum
(22-27)
frontend/__tests__/unit/utils/structuredData.test.ts (1)
frontend/src/types/user.ts (1)
User
(10-32)
frontend/__tests__/unit/components/ProgramCard.test.tsx (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
ProgramStatusEnum
(22-27)
🔇 Additional comments (80)
frontend/__tests__/unit/utils/structuredData.test.ts (2)
5-5
: LGTM: test fixture typed withUser
.
Type aligns withtypes/user.ts
.
26-31
: Confirm units for date normalization (seconds vs milliseconds).
The expected values imply seconds-based normalization. If the helper uses ms, adjust inputs (or expectations) accordingly.frontend/__tests__/unit/pages/MyMentorship.test.tsx (2)
1-1
: Switching hook import to '@apollo/client/react' is correct for Apollo v4.
Matches the new recommended subpath for React hooks.
8-14
: Ensure the component imports hooks from the same @apollo/client subpath you're mocking
You mock '@apollo/client/react' — that will not stub imports from '@apollo/client'. Either mock '@apollo/client' instead or change the component to import from '@apollo/client/react'. Automated search didn't locate the MyMentorship page in this repo; confirm the component's import path.frontend/__tests__/unit/pages/ProjectsHealthDashboardMetrics.test.tsx (2)
1-1
: LGTM: switched hooks to @apollo/client/reactCorrect entrypoint for Apollo v4 React hooks.
Please confirm the page under test also imports hooks from '@apollo/client/react' so this mock reliably intercepts calls.
6-9
: Mocking strategy looks correctSpreading the real module and overriding useQuery with a jest.fn is appropriate for these tests.
frontend/__tests__/unit/pages/ProjectDetails.test.tsx (2)
1-1
: Apollo v4 hooks entrypoint looks correct.Good switch to '@apollo/client/react'. Please ensure the component under test imports hooks from the same entrypoint; otherwise the mock won’t intercept.
8-11
: Mock target updated to the react entrypoint — LGTM.Spreading the actual exports and overriding useQuery keeps behavior consistent.
frontend/__tests__/unit/pages/EditModule.test.tsx (4)
20-24
: React-surface mocks look correct.Spreading actual and overriding
useQuery
/useMutation
is appropriate for this test.
63-82
: Query mock shape aligns with expected component usage.Admins and module fields look consistent for rendering and form population.
83-86
: Mutation mock wiring is fine.Returning
[fn, { loading: false }]
matches Apollo hook tuple.
127-127
: Loading-state mock is fine.This will exercise the spinner path as intended.
frontend/__tests__/unit/pages/ModuleDetails.test.tsx (2)
13-16
: Mocking the React entrypoint is aligned with the new import path.Good partial mock retaining real exports and overriding only useQuery.
1-1
: Correct Apollo v4 hook import path.LGTM — verified no React hooks are imported from the top-level '@apollo/client'; hooks are imported from '@apollo/client/react' in tests and app code, so the mock will intercept correctly.
frontend/__tests__/unit/pages/SnapshotDetails.test.tsx (6)
39-44
: Default mock shape LGTM.Covers data/loading/error for the happy path.
103-106
: LGTM.Happy path mock for navigation test is fine.
122-125
: LGTM.Chapter navigation test wiring is consistent.
141-144
: LGTM.Release rendering test wiring matches the new import surface.
157-167
: LGTM.Good negative-case coverage by zeroing the new* arrays.
1-1
: Correct v4 import path — approved.Client hooks are imported from '@apollo/client/react' across the repo; server-side imports from '@apollo/client' (ApolloClient/gql/links) are intentional. No stray hook imports found.
frontend/__tests__/unit/components/SingleModuleCard.test.tsx (1)
7-7
: Confirmed: generated enums export ExperienceLevelEnum and ProgramStatusEnumfrontend/src/types/generated/graphql.ts exports ExperienceLevelEnum and ProgramStatusEnum — the imports in the tests are correct.
frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx (4)
8-12
: Mock target updated to @apollo/client/react — aligned with the migration.Good move to mock the React entrypoint so it matches the new hook imports.
35-38
: Same refactor applies: reuse the typed mock helper.- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + mockedUseQuery.mockReturnValue({ loading: true, data: null, })
48-51
: Same refactor applies here as well.- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + mockedUseQuery.mockReturnValue({ loading: false, data: { program: null }, })
1-1
: Confirmed — mock will apply (both test and page use '@apollo/client/react').frontend/tests/unit/pages/ProgramDetailsMentorship.test.tsx mocks useQuery from '@apollo/client/react' and frontend/src/app/my/mentorship/programs/[programKey]/page.tsx imports useMutation/useQuery from '@apollo/client/react'; no hook imports from '@apollo/client' found in mentorship pages.
frontend/__tests__/unit/pages/CreateProgram.test.tsx (1)
1-1
: Correct Apollo v4 React entrypoint for hooksUsing '@apollo/client/react' aligns with Apollo Client v4’s React entrypoint and the PR’s migration approach.
frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx (1)
13-16
: Mock wiring looks correct.Mock path matches the import; spreading
requireActual
preserves other exports.frontend/__tests__/unit/pages/CommitteeDetails.test.tsx (7)
8-11
: Mocking the React entry and overriding useQuery looks good.Preserves actual exports while replacing useQuery with a jest.fn.
29-33
: Sane default mock shape for useQuery.Provides data and error defaults expected by the component.
64-67
: Good: error-path mock for “not found”.Matches the component’s expected error handling.
94-97
: Good: covers incomplete contributor names.Validates graceful fallback to login.
114-118
: Good: GraphQL error case exercised.Asserts user-facing message on request failure.
127-130
: Good: sponsor block expectation remains explicit.Keeps behavior pinned during migration.
1-1
: Correct import path for Apollo v4 hooks.Hook imports use '@apollo/client/react' now — verified: no remaining useQuery/useMutation/useApolloClient/etc. imports from '@apollo/client'.
frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx (3)
14-16
: Module mock updated to @apollo/client/react looks goodPreserving actual exports and overriding useQuery is correct for Apollo v4.
52-56
: Same DRY suggestion as above for repeated castsUse the mockUseQuery alias here as well.
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + ;mockUseQuery.mockReturnValue({
65-69
: Same DRY suggestion as above for repeated castsUse the mockUseQuery alias here too.
- ;(useQuery as unknown as jest.Mock).mockReturnValue({ + ;mockUseQuery.mockReturnValue({frontend/__tests__/unit/pages/ProjectsHealthDashboardOverview.test.tsx (1)
1-1
: Correct: import React hooks from @apollo/client/react in v4This aligns with Apollo Client v4 module structure.
frontend/__tests__/unit/pages/UserDetails.test.tsx (2)
11-12
: LGTM: Mocking the React entrypoint.Mock path matches the new hook import path.
1-1
: LGTM — Apollo v4 hook imports are correct.Searched repo (excluding tests) for useQuery/useLazyQuery/useMutation/useSubscription imported from '@apollo/client' — no matches found; hooks use '@apollo/client/react'.
frontend/__tests__/unit/pages/CreateModule.test.tsx (2)
1-2
: Correct v4 hook import path.Importing React hooks from @apollo/client/react matches Apollo v4 module boundaries.
19-25
: Mocks aligned with v4 entrypoint.Stubbing useQuery/useMutation/useApolloClient from @apollo/client/react is the right approach for Apollo v4.
frontend/__tests__/unit/pages/RepositoryDetails.test.tsx (6)
37-41
: CastinguseQuery
tojest.Mock
is fine here.
Keeps TS quiet during migration.
63-67
: Data-available mock looks fine.
149-152
: No-data case mock is reasonable.
198-211
: Zero-stats mock reads well for the “missing stats” scenario.
1-1
: Align imports to @apollo/client/react so the test mock appliesfrontend/tests/unit/pages/RepositoryDetails.test.tsx imports useQuery from '@apollo/client/react'; if the component still imports from '@apollo/client' the mock won’t intercept — verify and update all useQuery/useLazyQuery imports to '@apollo/client/react' across the repo.
8-11
: Mock target aligns with import path — sandbox couldn't confirm absence of '@apollo/client' hook imports.
Run locally to list any remaining root-package hook imports and paste the output:
rg -n --hidden -S "import\s+{[^}]\buse(?:Query|LazyQuery)\b[^}]}\s+from\s+['"]@apollo/client(?:/react)?['"]" -g '!node_modules' -g '!dist' -C2
If hits are found, standardize imports to '@apollo/client/react' or add a jest.mock for '@apollo/client' as well.frontend/__tests__/unit/pages/EditProgram.test.tsx (3)
14-21
: Module mock switched to '@apollo/client/react' — LGTMThe requireActual + override pattern is correct and aligns with the new import surface.
50-57
: Mock is correct — component expectsdata.getProgram
The component referencesdata?.getProgram
and destructuresconst { getProgram: program } = data
in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx, so the test mock usingdata.getProgram
is correct.
71-86
: Dates: mocks are correct — no change required.
formatDateForInput(new Date(dateStr) -> toISOString().slice(0,10)) accepts '2025-01-01' and '2025-12-31' and will produce the expected 'YYYY-MM-DD' values for the date inputs.frontend/__tests__/unit/pages/ProgramDetails.test.tsx (4)
1-1
: Correct Apollo v4 hook import path.Good switch to @apollo/client/react for hooks.
8-12
: Mocking the React entrypoint is correct.Targeting @apollo/client/react keeps mocks aligned with Apollo v4.
35-38
: Same aliasing suggestion as above.Use the mockUseQuery alias instead of re‑casting.
48-51
: Same aliasing suggestion as above.Use the mockUseQuery alias instead of re‑casting.
frontend/__tests__/unit/pages/Home.test.tsx (6)
11-14
: Mocks aligned with Apollo v4 module path.Looks good.
88-92
: Same aliasing suggestion as above.Use a mockUseQuery alias instead of re‑casting.
113-116
: Same aliasing suggestion as above.Use a mockUseQuery alias instead of re‑casting.
196-201
: Same aliasing suggestion as above.Use a mockUseQuery alias instead of re‑casting.
248-255
: Same aliasing suggestion as above.Use a mockUseQuery alias instead of re‑casting.
1-1
: Approved — hooks correctly imported from '@apollo/client/react'Repo-wide scan found no direct imports of useQuery/useMutation/etc. from '@apollo/client'. Server-side imports of gql/ApolloClient remain from '@apollo/client' (expected).
frontend/__tests__/unit/pages/About.test.tsx (12)
1-1
: Correct Apollo v4 hook import path.Good update to @apollo/client/react.
12-15
: Mocks target the right module.Mocking @apollo/client/react is correct for Apollo v4.
175-185
: Heads-up: ensure this mock matches the component’s document.If the page uses generated documents, prefer operation-name matching or comparing to the generated document as suggested above.
308-320
: Same note on document identity.Apply the operation-name or generated-document approach here as well.
339-351
: LGTM.This branch keys off variables only and doesn’t rely on document identity.
371-377
: LGTM.This mock variation is fine and doesn’t depend on query identity.
393-401
: LGTM.No identity checks; safe.
442-451
: LGTM.No identity checks; safe.
465-476
: LGTM.No identity checks; safe.
488-497
: LGTM.No identity checks; safe.
510-519
: LGTM.No identity checks; safe.
558-567
: Same issue as above for topContributors.Use operation-name or generated-document comparison.
frontend/__tests__/unit/data/mockProgramData.ts (2)
1-1
: Switch to generated enum source looks correct.Using types/generated/graphql aligns with codegen outputs.
20-20
: Enum member casing updated to match codegen.ProgramStatusEnum.Draft aligns with typical codegen (value 'DRAFT'); UI can still render “Draft”.
Confirm other tests referencing ProgramStatusEnum have updated member casing to avoid type errors.
frontend/__tests__/unit/components/ProgramCard.test.tsx (3)
5-5
: Importing ProgramStatusEnum from generated types is consistent.Good alignment with codegen.
Ensure the Program type (types/mentorship) is compatible with the generated enum type to avoid TS widening or mismatch.
36-36
: Updated enum member casing is correct.ProgramStatusEnum.Published matches codegen style.
299-299
: Updated enum member casing is correct.ProgramStatusEnum.Draft matches codegen style.
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (2)
2-2
: Correct Apollo v4 hook import path.useQuery/useMutation from @apollo/client/react is the right entrypoint.
11-15
: Mocks aligned to @apollo/client/react.Looks good.
jest.mock('@apollo/client', () => ({ | ||
gql: jest.requireActual('@apollo/client').gql, | ||
})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
🧩 Analysis chain
Don’t stub @apollo/client down to only gql — pass through actual exports to avoid breakage.
Limiting the @apollo/client mock to just gql can break wrappers/providers that import ApolloClient, InMemoryCache, ApolloProvider, HttpLink, etc. Either remove this mock or pass through the full actual module.
Apply one of these:
Option A — pass through actual module:
-jest.mock('@apollo/client', () => ({
- gql: jest.requireActual('@apollo/client').gql,
-}))
+jest.mock('@apollo/client', () => {
+ const actual = jest.requireActual('@apollo/client')
+ return { ...actual }
+})
Option B — remove the mock entirely (preferred if not strictly needed in this test):
-jest.mock('@apollo/client', () => ({
- gql: jest.requireActual('@apollo/client').gql,
-}))
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Checking if @apollo/client is used in test utilities or the page under test (would break if only gql is exported)…"
echo
echo "— Potential test utils matching 'testUtil' —"
fd -a -t f -p 'testUtil' | sed -n '1,200p' || true
echo
echo "— Imports from @apollo/client inside any test util files —"
fd -a -t f -p 'testUtil' | xargs -r rg -n -C2 -e "from ['\"]@apollo/client['\"]" -e "require\\(['\"]@apollo/client['\"]\\)" || true
echo
echo "— Imports from @apollo/client across app/ and wrappers/ —"
rg -n -C2 --type=ts --type=tsx -g 'app/**' -g 'wrappers/**' -e "from ['\"]@apollo/client['\"]" -e "require\\(['\"]@apollo/client['\"]\\)" || true
Length of output: 361
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate CreateModule.test.tsx"
fd -a -t f -I CreateModule.test.tsx || true
echo
echo "Show file header around the mock in CreateModule.test.tsx (if present)"
rg -n -C3 "jest\.mock\(['\"]@apollo/client['\"]" || true
# If match found, print the file with context
MATCH_FILE=$(rg -n --no-heading "jest\.mock\(['\"]@apollo/client['\"]" -S --hidden --glob '!node_modules/**' --line-number --pretty || true)
if [ -n "$MATCH_FILE" ]; then
FILE_PATH=$(echo "$MATCH_FILE" | sed -n '1p' | cut -d: -f1)
echo
echo "----- $FILE_PATH -----"
sed -n '1,160p' "$FILE_PATH" || true
fi
echo
echo "Show src/wrappers/testUtil.tsx (discovered earlier)"
if [ -f src/wrappers/testUtil.tsx ]; then
sed -n '1,240p' src/wrappers/testUtil.tsx || true
else
echo "src/wrappers/testUtil.tsx not found"
fi
echo
echo "Search for any imports from @apollo/client across the repo"
rg -n -C2 "@apollo/client" --hidden --glob '!node_modules/**' || true
echo
echo "Search for other jest.mock('@apollo/client' occurrences"
rg -n "jest\.mock\(['\"]@apollo/client['\"]" --hidden --glob '!node_modules/**' || true
Length of output: 1857
Don't mock @apollo/client down to only gql — pass through actual exports or remove the mock.
Mocking only gql can break code that imports other Apollo exports (ApolloClient, InMemoryCache, useApolloClient, ApolloProvider, HttpLink, etc.). Affected: frontend/tests/unit/pages/CreateModule.test.tsx (≈26–28) and frontend/tests/unit/pages/EditModule.test.tsx (≈26–28).
Apply one of these:
Option A — pass through actual module:
-jest.mock('@apollo/client', () => ({
- gql: jest.requireActual('@apollo/client').gql,
-}))
+jest.mock('@apollo/client', () => {
+ const actual = jest.requireActual('@apollo/client')
+ return { ...actual }
+})
Option B — remove the mock entirely (preferred if not strictly needed in this test):
-jest.mock('@apollo/client', () => ({
- gql: jest.requireActual('@apollo/client').gql,
-}))
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
jest.mock('@apollo/client', () => ({ | |
gql: jest.requireActual('@apollo/client').gql, | |
})) | |
jest.mock('@apollo/client', () => { | |
const actual = jest.requireActual('@apollo/client') | |
return { ...actual } | |
}) |
🤖 Prompt for AI Agents
In frontend/__tests__/unit/pages/CreateModule.test.tsx around lines 26 to 28,
the test currently replaces the entire @apollo/client module with an object
exposing only gql which breaks other imports (ApolloClient, InMemoryCache,
hooks, etc.); fix by removing the jest.mock call entirely or change it to a
pass-through mock so the real module exports are kept (i.e., import the actual
module and return it, optionally overriding only specific pieces you need to
stub), ensuring all other Apollo exports used by the tested components remain
available.
frontend/__tests__/unit/pages/ProjectHealthDashboardMetricsDetails.test.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (13)
frontend/__tests__/unit/utils/structuredData.test.ts (2)
1-1
: Useimport type
for type-only imports.Prevents emitting a runtime import and avoids TS 5
verbatimModuleSyntax
issues.Apply this diff:
-import { User } from 'types/user' +import type { User } from 'types/user'
29-30
: Derive expected ISO timestamps instead of hardcoding.000Z
.Reduces brittleness if formatting changes upstream.
Apply this diff:
it('should generate valid ProfilePage structured data', () => { const result = generateProfilePageStructuredData(mockUser) + const expectedDateCreated = new Date(mockUser.createdAt!).toISOString() + const expectedDateModified = new Date(mockUser.updatedAt!).toISOString() + expect(result).toEqual({ '@context': 'https://schema.org', '@type': 'ProfilePage', - dateCreated: '2020-01-01T00:00:00.000Z', - dateModified: '2021-02-03T00:00:00.000Z', + dateCreated: expectedDateCreated, + dateModified: expectedDateModified, mainEntity: {frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (4)
31-34
: Use generated mutation document; remove the TODO.Replace the legacy mutation constant with the generated document to get end-to-end typing and consistency with the query update.
- const [updateProgram] = useMutation(UPDATE_PROGRAM_STATUS_MUTATION, { - // TODO: update - onError: handleAppError, - }) + const [updateProgram] = useMutation(UpdateProgramStatusDocument, { + onError: handleAppError, + })Add the import (outside this hunk):
import { UpdateProgramStatusDocument } from 'types/__generated__/programsMutations.generated'
83-83
: Fix: calling toLowerCase on enum values can fail type-checking.Wrap the enum in String() for safe formatting.
- title: `Program status updated to ${upperFirst(newStatus.toLowerCase())}`, + title: `Program status updated to ${upperFirst(String(newStatus).toLowerCase())}`,
131-131
: Same enum-string formatting nit as above.- { label: 'Status', value: upperFirst(program.status.toLowerCase()) }, + { label: 'Status', value: upperFirst(String(program.status).toLowerCase()) },
13-13
: Optional: type state from generated query to avoid drift.Derive Program/modules state types from GetProgramAndModulesQuery to keep in lockstep with schema changes.
Example (outside this hunk):
import type { GetProgramAndModulesQuery } from 'types/__generated__/graphql' const [program, setProgram] = useState<GetProgramAndModulesQuery['getProgram'] | null>(null) const [modules, setModules] = useState<NonNullable<GetProgramAndModulesQuery['getProgramModules']>>([])frontend/__tests__/unit/pages/EditModule.test.tsx (1)
25-27
: Avoid over‑mocking @apollo/client root to only gql (can break other imports).This stub drops all other exports from @apollo/client at runtime. If the component (or its deps) import anything else (e.g., ApolloError), tests may break. Prefer pass‑through to the actual module.
Apply this diff:
-jest.mock('@apollo/client', () => ({ - gql: jest.requireActual('@apollo/client').gql, -})) +jest.mock('@apollo/client', () => jest.requireActual('@apollo/client'))Please confirm the component under test doesn’t rely on other @apollo/client exports at runtime.
frontend/src/components/SingleModuleCard.tsx (2)
40-40
: Avoidwindow
at render time; useusePathname()
to build URLsReferencing
window
can break SSR and hydration. PreferusePathname()
fromnext/navigation
.Apply within the selected lines:
- router.push(`${window.location.pathname}/modules/${module.key}`) + router.push(`${pathname}/modules/${module.key}`)- router.push(`${window.location.pathname}/modules/${module.key}/edit`) + router.push(`${pathname}/modules/${module.key}/edit`)- router.push(`${window.location.pathname}/modules/create`) + router.push(`${pathname}/modules/create`)- href={`${window.location.pathname}/modules/${module.key}`} + href={`${pathname}/modules/${module.key}`}And outside these ranges, also update:
// imports import { useRouter, usePathname } from 'next/navigation' // after const router = useRouter() const pathname = usePathname()Note: Aware of your preference to keep migration PRs focused. Consider as a small follow-up if out of scope.
Also applies to: 45-45, 50-50, 76-76
1-1
: Confirm client component usage or add 'use client'This file uses React/Next client hooks. Ensure it's only imported by client components, or add the directive.
'use client'
frontend/__tests__/unit/pages/About.test.tsx (4)
15-18
: Type the mocked useQuery to avoidunknown as jest.Mock
Use a typed alias so you don’t repeat casts and you get signatures checked.
Add once near the imports:
// At the top (after imports) const mockedUseQuery = useQuery as unknown as jest.MockedFunction<typeof useQuery>Then replace usages like:
-;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { +;mockedUseQuery.mockImplementation((query, options) => {(Apply similarly where you reassign the implementation in individual tests.)
119-136
: DRY up repeateduseQuery
mocksYou’re re-declaring similar mockImplementations many times. Extracting small helpers will reduce duplication and ease future changes.
Example helper (place near the top of the file):
type Key = string | undefined; const byKey = (key: Key, expected: Key) => key === expected; function defaultAboutUseQueryMock(query: any, options?: { variables?: { key?: string } }) { const key = options?.variables?.key; if (query === GetProjectMetadataDocument && byKey(key, 'nest')) return mockProjectData; if (query === GetTopContributorsDocument && byKey(key, 'nest')) return mockTopContributorsData; if (query === GetLeaderDataDocument) return mockUserData(key); return { loading: true }; }Then in beforeEach:
-;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { ... }) +mockedUseQuery.mockImplementation(defaultAboutUseQueryMock)And in specialized tests, override only the differing branch, delegating the rest to the default to avoid breaking unrelated queries.
Also applies to: 178-187, 311-323, 342-354, 373-381, 395-405, 445-454, 468-480, 491-501, 513-523, 535-545, 561-571
178-187
: Fix incorrect error shape in mockYou return
{ error: mockError }
, wheremockError
itself is{ error: Error }
. Apollo expectserror
to be an Error/ApolloError, not a nested object.Apply:
- } else if (options?.variables?.key === 'arkid15r') { - return { data: null, loading: false, error: mockError } + } else if (options?.variables?.key === 'arkid15r') { + return { data: null, loading: false, error: new Error('GraphQL error') }Alternatively, redefine
mockError
to be an Error:// near line 112 -const mockError = { - error: new Error('GraphQL error'), -} +const mockError = new Error('GraphQL error')
140-142
: Replace jest.clearAllMocks() with jest.resetAllMocks()clearAllMocks() doesn't reset mock implementations and can leak state across tests; resetAllMocks() clears calls and restores default implementations.
- jest.clearAllMocks() + jest.resetAllMocks()rg found 80 occurrences under frontend/tests; update this file and consider a project-wide replace or set "resetMocks" in Jest config — verify any tests that rely on preserved mock implementations before changing.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
frontend/__tests__/unit/data/mockProgramData.ts
(2 hunks)frontend/__tests__/unit/pages/About.test.tsx
(13 hunks)frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
(5 hunks)frontend/__tests__/unit/pages/CreateModule.test.tsx
(3 hunks)frontend/__tests__/unit/pages/EditModule.test.tsx
(5 hunks)frontend/__tests__/unit/utils/structuredData.test.ts
(3 hunks)frontend/src/app/mentorship/programs/[programKey]/page.tsx
(3 hunks)frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
(4 hunks)frontend/src/components/SingleModuleCard.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/tests/unit/pages/CreateModule.test.tsx
- frontend/tests/unit/pages/ApiKeysPage.test.tsx
- frontend/src/app/mentorship/programs/[programKey]/page.tsx
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/__tests__/unit/pages/EditModule.test.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/__tests__/unit/pages/EditModule.test.tsx
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
🧬 Code graph analysis (4)
frontend/__tests__/unit/utils/structuredData.test.ts (1)
frontend/src/types/user.ts (1)
User
(10-32)
frontend/__tests__/unit/data/mockProgramData.ts (1)
backend/apps/mentorship/api/internal/nodes/enum.py (1)
ProgramStatusEnum
(22-27)
frontend/__tests__/unit/pages/About.test.tsx (2)
frontend/src/types/__generated__/projectQueries.generated.ts (2)
GetProjectMetadataDocument
(37-37)GetTopContributorsDocument
(38-38)frontend/src/types/__generated__/userQueries.generated.ts (1)
GetLeaderDataDocument
(26-26)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/app/global-error.tsx (1)
handleAppError
(66-86)frontend/src/types/__generated__/programsQueries.generated.ts (1)
GetProgramAndModulesDocument
(37-37)
🔇 Additional comments (17)
frontend/__tests__/unit/utils/structuredData.test.ts (2)
5-5
: Switch toUser
type looks correct.Matches the exported type in
types/user.ts
.
61-61
: LGTM on minimal user typing.Covers required fields; optional fields omitted as intended.
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5)
2-2
: Apollo v4 hooks import looks good.Switching hooks to '@apollo/client/react' aligns with the v4 migration.
10-11
: Good move to generated enums/documents.Using ProgramStatusEnum and GetProgramAndModulesDocument is consistent with codegen-driven v4 usage.
40-40
: Query migration to generated document is correct.Typed variables + skip usage are appropriate.
79-79
: RefetchQuery shape is valid in v4; consider awaiting if UI depends on it.Only add
awaitRefetchQueries: true
if you need the refetch to complete before proceeding (e.g., before reading cache/UI that depends on fresh data).
132-133
: Duplicate of earlier feedback: guard/normalize date scalar before formatting.Previously flagged: formatDate may throw if startedAt/endedAt aren’t reliably strings. Consider a presence check or scalar mapping in codegen to ensure these are strings.
frontend/__tests__/unit/pages/EditModule.test.tsx (5)
1-1
: Good: Hooks imported from @apollo/client/react align with Apollo v4 and your mocks.
19-23
: Good: Mocking @apollo/client/react directly avoids the prior import/mock mismatch.
61-80
: Mock shape for useQuery looks correct for this test flow.
81-84
: Mutation mock wiring is correct and verifies the submit path.
125-125
: Loading state mock aligns with the spinner assertion.frontend/__tests__/unit/data/mockProgramData.ts (1)
1-1
: Confirm generated enum export and Jest path alias
- Confirm ProgramStatusEnum is emitted as an exported enum (not generated as a type via enumsAsTypes).
- Confirm the 'types/*' tsconfig path alias resolves under Jest (moduleNameMapper/ts-jest); check frontend/tests/unit/data/mockProgramData.ts — import { ProgramStatusEnum } from 'types/generated/graphql'
frontend/__tests__/unit/pages/About.test.tsx (4)
1-1
: Apollo v4 hooks import path is correctSwitching to '@apollo/client/react' aligns with Apollo Client v4. Looks good.
535-545
: LGTM: Project error toast path is validated correctlyMatching the query by GetProjectMetadataDocument and asserting addToast payload looks solid.
561-571
: LGTM: Top contributors error toast path is validatedSame pattern as the project error case; assertions look correct.
9-14
: Good move — About page uses generated DocumentNodesfrontend/src/app/about/page.tsx imports and calls GetProjectMetadataDocument, GetTopContributorsDocument, and GetLeaderDataDocument with useQuery, so tests' identity checks remain stable.
|
||
const moduleDetails = [ | ||
{ label: 'Experience Level', value: upperFirst(module.experienceLevel) }, | ||
{ label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against null/undefined before calling toLowerCase()
This will throw if module.experienceLevel
is nullish. Keep the existing "Unknown" fallback by returning undefined
when absent.
- { label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) },
+ { label: 'Experience Level', value: module.experienceLevel ? upperFirst(module.experienceLevel.toLowerCase()) : undefined },
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{ label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) }, | |
{ label: 'Experience Level', value: module.experienceLevel ? upperFirst(module.experienceLevel.toLowerCase()) : undefined }, |
🤖 Prompt for AI Agents
In frontend/src/components/SingleModuleCard.tsx around line 54, the expression
calls module.experienceLevel.toLowerCase() without guarding for null/undefined
which will throw; change it to first check that module.experienceLevel is
non-nullish and only then call toLowerCase and upperFirst, otherwise return
undefined to preserve the existing "Unknown" fallback (e.g., use a conditional
or optional chaining with a nullish check so the value becomes
upperFirst(module.experienceLevel.toLowerCase()) when present, or undefined when
absent).
sorry, accidentally clicked "Ready for review". I still have some more refactoring to do.
I also have to verify all the changes. This is not ready for review yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (12)
frontend/__tests__/unit/utils/structuredData.test.ts (1)
1-1
: Prefer type-only import forUser
in frontend/tests/unit/utils/structuredData.test.tsUse
import type
so the symbol is elided at compile time and avoids unintended runtime imports.-import { User } from 'types/user' +import type { User } from 'types/user'frontend/__tests__/unit/pages/About.test.tsx (5)
178-188
: Return an Error instance, not an object containing one.The current branch returns
{ error: { error: Error } }
, which can break code expectingerror instanceof Error
.Apply this diff:
- } else if (options?.variables?.key === 'arkid15r') { - return { data: null, loading: false, error: mockError } + } else if (options?.variables?.key === 'arkid15r') { + return { data: null, loading: false, error: new Error('GraphQL error') }
468-480
: Usenull
(orundefined
) forerror
, notfalse
.A boolean
false
forerror
can cause type mismatches and brittle checks. Prefernull
.Apply this diff:
- } else if (options?.variables?.key === 'arkid15r') { - return { data: null, loading: false, error: false } + } else if (options?.variables?.key === 'arkid15r') { + return { data: null, loading: false, error: null }
513-523
: Optional: qualify by document for stricter shaping.You’re matching only on
key === 'nest'
, which also affects non-project queries. Guarding by the specific document avoids returning the wrong data shape to other queries.Apply this diff:
- ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { - if (options?.variables?.key === 'nest') { - return { loading: false, data: { project: null }, error: null } - } + ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { + if (query === GetProjectMetadataDocument && options?.variables?.key === 'nest') { + return { loading: false, data: { project: null }, error: null } + } return { loading: false, data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } }, error: null, } })
535-544
: Avoid returning mismatched shapes for other queries in this override.Currently the fallback returns
{ data: { user: ... } }
even for non‑user queries, which can break consumers expectingproject
/topContributors
.Apply this diff:
- ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { - if (query === GetProjectMetadataDocument && options?.variables?.key === 'nest') { - return { loading: false, data: null, error: new Error('GraphQL error') } - } - return { - loading: false, - data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } }, - error: null, - } - }) + ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { + const key = options?.variables?.key + if (query === GetProjectMetadataDocument && key === 'nest') { + return { loading: false, data: null, error: new Error('GraphQL error') } + } + if (query === GetTopContributorsDocument && key === 'nest') { + return { loading: false, data: { topContributors: [] }, error: null } + } + if (query === GetLeaderDataDocument) { + return { + loading: false, + data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } }, + error: null, + } + } + return { loading: true } + })
561-570
: Same here: return shapes matching the queried document.Ensure project, topContributors, and user queries each receive compatible data.
Apply this diff:
- ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { - if (query === GetTopContributorsDocument && options?.variables?.key === 'nest') { - return { loading: false, data: null, error: new Error('GraphQL error') } - } - return { - loading: false, - data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } }, - error: null, - } - }) + ;(useQuery as unknown as jest.Mock).mockImplementation((query, options) => { + const key = options?.variables?.key + if (query === GetTopContributorsDocument && key === 'nest') { + return { loading: false, data: null, error: new Error('GraphQL error') } + } + if (query === GetProjectMetadataDocument && key === 'nest') { + return mockProjectData + } + if (query === GetLeaderDataDocument) { + return { + loading: false, + data: { user: { avatarUrl: '', company: '', name: 'Dummy', location: '' } }, + error: null, + } + } + return { loading: true } + })frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (3)
31-34
: Resolve TODO: migrate mutation to generated document/types.Follow the same pattern as queries (use UpdateProgramStatusDocument and typed variables) when ready.
I can draft the minimal mutation migration if you want it in this PR.
79-79
: Optionally await refetchQueries to ensure UI consistency before toast.If you want the toast after fresh data is in cache, add awaitRefetchQueries: true.
Apply within the mutate call:
- refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }], + refetchQueries: [{ query: GetProgramAndModulesDocument, variables: { programKey } }], + awaitRefetchQueries: true,
83-83
: Type-safe enum formatting: cast before toLowerCase.Depending on codegen (enum vs union), ProgramStatusEnum may not have string methods at the type level.
Use a safe cast:
- title: `Program status updated to ${upperFirst(newStatus.toLowerCase())}`, + title: `Program status updated to ${upperFirst(String(newStatus).toLowerCase())}`,frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (3)
54-55
: Tighten mock typings (optional).
Usejest.MockedFunction
to avoidunknown
casts.-const mockUseQuery = useQuery as unknown as jest.Mock -const mockUseMutation = useMutation as unknown as jest.Mock +const mockUseQuery = useQuery as jest.MockedFunction<typeof useQuery> +const mockUseMutation = useMutation as jest.MockedFunction<typeof useMutation>
71-79
: Document identity checks can be brittle; prefer matching by operation name (optional).
In some build/test setups the same doc can be re-instantiated, breaking strict equality.- mockUseMutation.mockImplementation((mutation) => { - if (mutation === CreateApiKeyDocument) { + mockUseMutation.mockImplementation((mutation: any) => { + const opName = mutation?.definitions?.[0]?.name?.value + if (opName === 'CreateApiKey') { return [mockCreateMutation, { loading: false }] } - if (mutation === RevokeApiKeyDocument) { + if (opName === 'RevokeApiKey') { return [mockRevokeMutation, { loading: false }] } return [jest.fn(), { loading: false }] })
198-205
: Reduce potential timezone flakiness in ISO expectation (optional).
Hard-coding the full ISO string can be fragile across environments. Loosen to prefix match.- expiresAt: new Date('2025-12-31T00:00:00.000Z').toISOString(), + expiresAt: expect.stringMatching(/^2025-12-31/),
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
frontend/__tests__/unit/data/mockProgramData.ts
(2 hunks)frontend/__tests__/unit/pages/About.test.tsx
(13 hunks)frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
(5 hunks)frontend/__tests__/unit/pages/CreateModule.test.tsx
(3 hunks)frontend/__tests__/unit/pages/EditModule.test.tsx
(5 hunks)frontend/__tests__/unit/utils/structuredData.test.ts
(3 hunks)frontend/src/app/mentorship/programs/[programKey]/page.tsx
(3 hunks)frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
(4 hunks)frontend/src/components/SingleModuleCard.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- frontend/tests/unit/data/mockProgramData.ts
- frontend/tests/unit/pages/CreateModule.test.tsx
- frontend/src/app/mentorship/programs/[programKey]/page.tsx
- frontend/tests/unit/pages/EditModule.test.tsx
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details. These queries cannot be removed or merged as they serve different use cases in the application.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS and GET_PROGRAM_ADMIN_DETAILS are two separate queries serving different purposes: GET_PROGRAM_DETAILS fetches comprehensive program information while GET_PROGRAM_ADMIN_DETAILS fetches only admin-related details.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-08T17:24:36.501Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/program.py:41-44
Timestamp: 2025-07-08T17:24:36.501Z
Learning: In the mentorship program GraphQL mutations, date validation is handled at the GraphQL schema/node level in the input types (CreateProgramInput, UpdateProgramInput), preventing null values from reaching the mutation logic where date comparisons are performed.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.
Applied to files:
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
📚 Learning: 2025-07-12T17:36:57.255Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/__tests__/unit/pages/createProgram.test.tsx:70-86
Timestamp: 2025-07-12T17:36:57.255Z
Learning: When testing React page components that use mocked form components, validation logic should be tested at the form component level, not the page level. Page-level tests should focus on authentication, role checking, submission handling, and navigation logic.
Applied to files:
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
🧬 Code graph analysis (4)
frontend/__tests__/unit/utils/structuredData.test.ts (1)
frontend/src/types/user.ts (1)
User
(10-32)
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (2)
frontend/src/app/global-error.tsx (1)
handleAppError
(66-86)frontend/src/types/__generated__/programsQueries.generated.ts (1)
GetProgramAndModulesDocument
(37-37)
frontend/__tests__/unit/pages/ApiKeysPage.test.tsx (1)
frontend/src/types/__generated__/apiKeyQueries.generated.ts (2)
CreateApiKeyDocument
(26-26)RevokeApiKeyDocument
(27-27)
frontend/__tests__/unit/pages/About.test.tsx (3)
frontend/src/types/__generated__/projectQueries.generated.ts (2)
GetProjectMetadataDocument
(37-37)GetTopContributorsDocument
(38-38)frontend/__tests__/unit/data/mockProjectData.ts (1)
mockProjectData
(1-26)frontend/src/types/__generated__/userQueries.generated.ts (1)
GetLeaderDataDocument
(26-26)
🔇 Additional comments (23)
frontend/src/components/SingleModuleCard.tsx (1)
54-54
: Guard against null/undefined before calling toLowerCase()This throws if
module.experienceLevel
is nullish. Preserve the existing "Unknown" fallback by returningundefined
when absent.- { label: 'Experience Level', value: upperFirst(module.experienceLevel.toLowerCase()) }, + { label: 'Experience Level', value: module.experienceLevel ? upperFirst(module.experienceLevel.toLowerCase()) : undefined },frontend/__tests__/unit/utils/structuredData.test.ts (3)
5-5
: LGTM: typingmockUser
withUser
aligns with the updated util signature
29-30
: LGTM: ISO8601 with millisecond precision matches the new formatting behavior
61-61
: LGTM:minimalUser
typed asUser
keeps the fallback-to-login case clearfrontend/__tests__/unit/pages/About.test.tsx (10)
1-1
: Correct Apollo v4 React import path.Importing hooks from '@apollo/client/react' is the right target for v4 React APIs.
9-14
: Good switch to generated documents.Using GetProjectMetadataDocument, GetTopContributorsDocument, and GetLeaderDataDocument aligns tests with graphql-codegen outputs.
15-18
: Appropriate mocking of React hooks entrypoint.Mocking '@apollo/client/react' and overriding useQuery is consistent with the migration.
119-136
: Baseline mock aligned to v4 doc identity checks.Document equality checks and variable-based routing look good.
311-323
: Loading-branch mock reads clearly.Accurately simulates a single leader in loading state.
342-354
: Null user data branch is fine.Correctly exercises the null-handling path.
373-381
: Project null branch is fine.This matches the error view flow for missing project data.
395-405
: Undefined user data case covered.Good coverage for an undefined payload edge case.
445-455
: Partial user data case covered.Good fallback expectation (login when name is missing).
491-501
: Loading spinner setup looks good.Mocks return loading for project; others return stable user data.
frontend/src/app/my/mentorship/programs/[programKey]/page.tsx (5)
10-11
: Good switch to generated GraphQL artifacts.Enum + document-based API adoption aligns with Apollo v4 and codegen.
13-13
: Confirm local types match generated query shape.Using Program/Module from 'types/mentorship' is OK, but please verify they align with GetProgramAndModules results to prevent subtle drift.
40-40
: LGTM: document-based useQuery.Correct migration to the generated DocumentNode.
131-131
: LGTM on status normalization.Formatting via lowerCase + upperFirst is fine.
Note: As previously flagged, adjacent date fields (Lines 132-133) may be null/unknown from the API; guarding before formatDate would harden runtime. Acknowledging this may be out-of-scope for the migration PR.
2-2
: Standardize Apollo import path; verify v4 subpath usage.
Using '@apollo/client/react' is fine in Apollo Client v4 — ensure all React-hook imports use the same subpath to avoid duplicate bundles and mocking friction.
Observed in frontend/src/app/my/mentorship/programs/[programKey]/page.tsx
Run locally to audit mixed imports:# from repo root git grep -n "@apollo/client" || true # or (if using ripgrep) rg -n -S "@apollo/client" -g '!node_modules/**' -g '!dist/**' || truefrontend/__tests__/unit/pages/ApiKeysPage.test.tsx (4)
9-12
: Switch to generated DocumentNodes is on point.
Imports align with codegen outputs and Apollo v4 doc-based workflow.
14-18
: Mock path updated correctly; confirm Jest hoisting in your setup.
This pattern relies on Jest hoistingjest.mock
. If running ESM without hoisting, move this mock above imports or convert to an explicit factory mock.
175-176
: Correct to assert DateTime variable as string.
Matches typical GraphQL DateTime transport semantics.
2-2
: Apollo v4 hooks import path is correct — no leftover hook imports from '@apollo/client' found.Repo-wide search: hooks (useQuery/useMutation/useApolloClient/etc.) are imported from '@apollo/client/react'; remaining '@apollo/client' usages are gql/ApolloClient (expected). Tests frontend/tests/unit/pages/EditModule.test.tsx and CreateModule.test.tsx mock '@apollo/client' to expose gql via requireActual — acceptable.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
frontend/__tests__/unit/components/ProgramActions.test.tsx (1)
69-87
: Assertions now use enum values — good; consider passing enum to props tooUsing ProgramStatusEnum in expectations is correct. To avoid string/enum drift, pass enum values to the component’s status prop in these tests as well.
Apply this diff in this file to pass enum values (repeat the pattern for remaining cases):
- render(<ProgramActions status="DRAFT" setStatus={setStatus} />) + render(<ProgramActions status={ProgramStatusEnum.Draft} setStatus={setStatus} />) - render(<ProgramActions status="PUBLISHED" setStatus={setStatus} />) + render(<ProgramActions status={ProgramStatusEnum.Published} setStatus={setStatus} />)frontend/src/components/ProgramActions.tsx (1)
10-15
: Type the status prop as ProgramStatusEnum and simplify setStatus typeYou compare against ProgramStatusEnum.* but type status as string. Make it ProgramStatusEnum for stronger safety. The union for setStatus is unnecessary if those are the only members.
Apply:
interface ProgramActionsProps { - status: string - setStatus: ( - newStatus: ProgramStatusEnum.Draft | ProgramStatusEnum.Published | ProgramStatusEnum.Completed - ) => void + status: ProgramStatusEnum + setStatus: (newStatus: ProgramStatusEnum) => void }frontend/__tests__/unit/pages/EditProgram.test.tsx (1)
39-39
: Reduce repeated casts for useQuery mocksDefine a typed alias once to avoid (unknown as jest.Mock) repetition.
Example:
-import { useQuery } from '@apollo/client/react' +import { useQuery } from '@apollo/client/react' +const mockedUseQuery = useQuery as unknown as jest.Mock ... -;(useQuery as unknown as jest.Mock).mockReturnValue({ loading: true }) +mockedUseQuery.mockReturnValue({ loading: true })Also applies to: 51-59, 72-87
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
frontend/__tests__/unit/components/ProgramActions.test.tsx
(3 hunks)frontend/__tests__/unit/pages/EditModule.test.tsx
(6 hunks)frontend/__tests__/unit/pages/EditProgram.test.tsx
(6 hunks)frontend/src/app/my/mentorship/page.tsx
(3 hunks)frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
(4 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
(3 hunks)frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
(3 hunks)frontend/src/components/ModuleForm.tsx
(3 hunks)frontend/src/components/ProgramActions.tsx
(3 hunks)frontend/src/types/mentorship.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- frontend/src/app/my/mentorship/programs/[programKey]/modules/create/page.tsx
- frontend/src/components/ModuleForm.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
- frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/edit/page.tsx
- frontend/src/app/my/mentorship/page.tsx
- frontend/src/types/mentorship.ts
- frontend/tests/unit/pages/EditModule.test.tsx
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/about/page.tsx:47-53
Timestamp: 2025-09-21T11:34:33.377Z
Learning: User rudransh-shrivastava prefers to keep Apollo Client migration PRs focused solely on migration-related changes and not include additional bug fixes or improvements that aren't directly related to the migration itself.
📚 Learning: 2025-08-31T13:47:15.861Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2155
File: frontend/src/server/queries/programsQueries.ts:81-81
Timestamp: 2025-08-31T13:47:15.861Z
Learning: In frontend/src/server/queries/programsQueries.ts, GET_PROGRAM_DETAILS is actively used in frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx for program editing functionality and cannot be removed. It serves a different purpose than GET_PROGRAM_ADMIN_DETAILS, providing comprehensive program information needed for editing.
Applied to files:
frontend/__tests__/unit/pages/EditProgram.test.tsx
📚 Learning: 2025-09-21T17:04:48.154Z
Learnt from: rudransh-shrivastava
PR: OWASP/Nest#2178
File: frontend/src/app/snapshots/[id]/page.tsx:0-0
Timestamp: 2025-09-21T17:04:48.154Z
Learning: User rudransh-shrivastava confirmed that suggested type safety improvements during Apollo Client migration were no longer relevant, reinforcing their preference to keep migration PRs focused on core migration changes rather than additional improvements.
Applied to files:
frontend/__tests__/unit/pages/EditProgram.test.tsx
📚 Learning: 2025-07-13T07:31:06.511Z
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: frontend/src/components/ModuleCard.tsx:53-55
Timestamp: 2025-07-13T07:31:06.511Z
Learning: In Next.js 13+ app router, useRouter from 'next/navigation' does not provide asPath or query properties. Use useParams to extract route parameters and usePathname to get the current pathname instead.
Applied to files:
frontend/__tests__/unit/pages/EditProgram.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run frontend e2e tests
🔇 Additional comments (5)
frontend/__tests__/unit/components/ProgramActions.test.tsx (1)
5-5
: LGTM: generated enum import is correctEnum import aligns with codegen changes.
frontend/src/components/ProgramActions.tsx (2)
30-38
: LGTM: setting enum values on actionsSwitching to ProgramStatusEnum.* for state transitions is correct and consistent with codegen.
46-52
: LGTM: option visibility checks using enumsConditions read clearly and align with the updated enum semantics.
Confirm that all call sites now pass ProgramStatusEnum (or the matching string values) until the prop type is tightened per the above diff.
frontend/__tests__/unit/pages/EditProgram.test.tsx (2)
1-1
: LGTM: import Apollo React hooks from @apollo/client/reactMatches Apollo v4 guidance.
15-22
: LGTM: mocking the React entrypointMocking @apollo/client/react (and spreading the actual) is correct for v4.
Resolves #2120
Proposed change
Migrate Apollo Client to v4
apollo/client
to 4.0.0typed-document-node
queriescreatedAt
/updatedAt
) fields to benumber | string
enums
to beUPPERCASE
as per conventionChecklist
make check-test
locally; all checks and tests passed.